temml 0.9.2 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
package/dist/temml.mjs CHANGED
@@ -666,7 +666,7 @@ class TextNode {
666
666
 
667
667
  /**
668
668
  * Converts the text node into a string
669
- * (representing the text iteself).
669
+ * (representing the text itself).
670
670
  */
671
671
  toText() {
672
672
  return this.text;
@@ -843,7 +843,6 @@ defineSymbol(math, rel, "\u226a", "\\ll", true);
843
843
  defineSymbol(math, rel, "\u226b", "\\gg", true);
844
844
  defineSymbol(math, rel, "\u224d", "\\asymp", true);
845
845
  defineSymbol(math, rel, "\u2225", "\\parallel");
846
- defineSymbol(math, rel, "\u22c8", "\\bowtie", true);
847
846
  defineSymbol(math, rel, "\u2323", "\\smile", true);
848
847
  defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
849
848
  defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
@@ -1160,7 +1159,6 @@ defineSymbol(math, rel, "\u22d9", "\\gggtr");
1160
1159
  defineSymbol(math, bin, "\u22b2", "\\lhd");
1161
1160
  defineSymbol(math, bin, "\u22b3", "\\rhd");
1162
1161
  defineSymbol(math, rel, "\u2242", "\\eqsim", true);
1163
- defineSymbol(math, rel, "\u22c8", "\\Join");
1164
1162
  defineSymbol(math, rel, "\u2251", "\\Doteq", true);
1165
1163
  defineSymbol(math, rel, "\u297d", "\\strictif", true);
1166
1164
  defineSymbol(math, rel, "\u297c", "\\strictfi", true);
@@ -1186,6 +1184,11 @@ defineSymbol(math, bin, "\u22ba", "\\intercal", true);
1186
1184
  defineSymbol(math, bin, "\u22d2", "\\doublecap");
1187
1185
  defineSymbol(math, bin, "\u22d3", "\\doublecup");
1188
1186
  defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
1187
+ defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
1188
+ defineSymbol(math, bin, "\u22c8", "\\Join");
1189
+ defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
1190
+ defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
1191
+ defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
1189
1192
 
1190
1193
  // AMS Arrows
1191
1194
  // Note: unicode-math maps \u21e2 to their own function \rightdasharrow.
@@ -1232,6 +1235,8 @@ defineSymbol(math, textord, "\u2018", "`");
1232
1235
  defineSymbol(math, textord, "$", "\\$");
1233
1236
  defineSymbol(text, textord, "$", "\\$");
1234
1237
  defineSymbol(text, textord, "$", "\\textdollar");
1238
+ defineSymbol(math, textord, "¢", "\\cent");
1239
+ defineSymbol(text, textord, "¢", "\\cent");
1235
1240
  defineSymbol(math, textord, "%", "\\%");
1236
1241
  defineSymbol(text, textord, "%", "\\%");
1237
1242
  defineSymbol(math, textord, "_", "\\_");
@@ -1888,7 +1893,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
1888
1893
  }
1889
1894
 
1890
1895
  /**
1891
- * This file converts a parse tree into a cooresponding MathML tree. The main
1896
+ * This file converts a parse tree into a corresponding MathML tree. The main
1892
1897
  * entry point is the `buildMathML` function, which takes a parse tree from the
1893
1898
  * parser.
1894
1899
  */
@@ -3146,13 +3151,12 @@ defineFunction({
3146
3151
  names: ["\\\\"],
3147
3152
  props: {
3148
3153
  numArgs: 0,
3149
- numOptionalArgs: 1,
3150
- argTypes: ["size"],
3154
+ numOptionalArgs: 0,
3151
3155
  allowedInText: true
3152
3156
  },
3153
3157
 
3154
3158
  handler({ parser }, args, optArgs) {
3155
- const size = optArgs[0];
3159
+ const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null;
3156
3160
  const newLine = !parser.settings.displayMode;
3157
3161
  return {
3158
3162
  type: "cr",
@@ -3520,6 +3524,11 @@ const delimiters = [
3520
3524
  "."
3521
3525
  ];
3522
3526
 
3527
+ // Export isDelimiter for benefit of parser.
3528
+ const dels = ["}", "\\left", "\\middle", "\\right"];
3529
+ const isDelimiter = str => str.length > 0 &&
3530
+ (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
3531
+
3523
3532
  // Metrics of the different sizes. Found by looking at TeX's output of
3524
3533
  // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
3525
3534
  // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
@@ -4912,7 +4921,6 @@ defineFunction({
4912
4921
  "\\mathscr",
4913
4922
  "\\mathsf",
4914
4923
  "\\mathtt",
4915
- "\\oldstylenums",
4916
4924
 
4917
4925
  // aliases
4918
4926
  "\\Bbb",
@@ -6203,10 +6211,6 @@ const noSuccessor = ["\\smallint"];
6203
6211
  // Math operators (e.g. \sin) need a space between these types and themselves:
6204
6212
  const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6205
6213
 
6206
- const dels$1 = ["}", "\\left", "\\middle", "\\right"];
6207
- const isDelimiter$1 = str => str.length > 0 &&
6208
- (delimiters.includes(str) || delimiterSizes[str] || dels$1.includes(str));
6209
-
6210
6214
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6211
6215
  // "supsub" since some of them (like \int) can affect super/subscripting.
6212
6216
 
@@ -6427,7 +6431,7 @@ defineFunction({
6427
6431
  parentIsSupSub: false,
6428
6432
  symbol: false,
6429
6433
  stack: false,
6430
- isFollowedByDelimiter: isDelimiter$1(next),
6434
+ isFollowedByDelimiter: isDelimiter(next),
6431
6435
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6432
6436
  name: funcName
6433
6437
  };
@@ -6452,7 +6456,7 @@ defineFunction({
6452
6456
  parentIsSupSub: false,
6453
6457
  symbol: false,
6454
6458
  stack: false,
6455
- isFollowedByDelimiter: isDelimiter$1(next),
6459
+ isFollowedByDelimiter: isDelimiter(next),
6456
6460
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6457
6461
  name: funcName
6458
6462
  };
@@ -6538,11 +6542,7 @@ function defineMacro(name, body) {
6538
6542
  _macros[name] = body;
6539
6543
  }
6540
6544
 
6541
- const dels = ["}", "\\left", "\\middle", "\\right"];
6542
- const isDelimiter = str => str.length > 0 &&
6543
- (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
6544
-
6545
- // NOTE: Unlike most builders, this one handles not only
6545
+ // NOTE: Unlike most builders, this one handles not only
6546
6546
  // "operatorname", but also "supsub" since \operatorname* can
6547
6547
  // affect super/subscripting.
6548
6548
 
@@ -6552,8 +6552,12 @@ const mathmlBuilder$1 = (group, style) => {
6552
6552
  // Is expression a string or has it something like a fraction?
6553
6553
  let isAllString = true; // default
6554
6554
  for (let i = 0; i < expression.length; i++) {
6555
- const node = expression[i];
6555
+ let node = expression[i];
6556
6556
  if (node instanceof mathMLTree.MathNode) {
6557
+ if (node.type === "mrow" && node.children.length === 1 &&
6558
+ node.children[0] instanceof mathMLTree.MathNode) {
6559
+ node = node.children[0];
6560
+ }
6557
6561
  switch (node.type) {
6558
6562
  case "mi":
6559
6563
  case "mn":
@@ -6611,7 +6615,9 @@ const mathmlBuilder$1 = (group, style) => {
6611
6615
  let wrapper;
6612
6616
  if (isAllString) {
6613
6617
  wrapper = new mathMLTree.MathNode("mi", expression);
6614
- wrapper.setAttribute("mathvariant", "normal");
6618
+ if (expression[0].text.length === 1) {
6619
+ wrapper.setAttribute("mathvariant", "normal");
6620
+ }
6615
6621
  } else {
6616
6622
  wrapper = new mathMLTree.MathNode("mrow", expression);
6617
6623
  }
@@ -7149,6 +7155,7 @@ defineFunctionBuilders({
7149
7155
  let isOver;
7150
7156
  let isSup;
7151
7157
  let appendApplyFunction = false;
7158
+ let appendSpace = false;
7152
7159
  let needsLeadingSpace = false;
7153
7160
 
7154
7161
  if (group.base && group.base.type === "horizBrace") {
@@ -7163,6 +7170,7 @@ defineFunctionBuilders({
7163
7170
  (group.base.type === "op" || group.base.type === "operatorname")) {
7164
7171
  group.base.parentIsSupSub = true;
7165
7172
  appendApplyFunction = !group.base.symbol;
7173
+ appendSpace = appendApplyFunction && !group.isFollowedByDelimiter;
7166
7174
  needsLeadingSpace = group.base.needsLeadingSpace;
7167
7175
  }
7168
7176
 
@@ -7250,6 +7258,11 @@ defineFunctionBuilders({
7250
7258
  } else {
7251
7259
  node = mathMLTree.newDocumentFragment([node, operator]);
7252
7260
  }
7261
+ if (appendSpace) {
7262
+ const space = new mathMLTree.MathNode("mspace");
7263
+ space.setAttribute("width", "0.1667em"); // thin space.
7264
+ node.children.push(space);
7265
+ }
7253
7266
  } else if (symbolRegEx.test(nodeType)) {
7254
7267
  // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
7255
7268
  node = new mathMLTree.MathNode("mrow", [node]);
@@ -7326,8 +7339,7 @@ const fontMap = {
7326
7339
  mathfrak: "fraktur",
7327
7340
  mathscr: "script",
7328
7341
  mathsf: "sans-serif",
7329
- mathtt: "monospace",
7330
- oldstylenums: "oldstylenums"
7342
+ mathtt: "monospace"
7331
7343
  };
7332
7344
 
7333
7345
  /**
@@ -7397,8 +7409,6 @@ const getVariant = function(group, style) {
7397
7409
  return "sans-serif"
7398
7410
  case "mathtt":
7399
7411
  return "monospace"
7400
- case "oldstylenums":
7401
- return "oldstylenums"
7402
7412
  }
7403
7413
 
7404
7414
  let text = group.text;
@@ -7694,10 +7704,7 @@ defineFunctionBuilders({
7694
7704
  let node;
7695
7705
  if (numberRegEx$1.test(group.text)) {
7696
7706
  const tag = group.mode === "text" ? "mtext" : "mn";
7697
- if (variant === "oldstylenums") {
7698
- const ms = new mathMLTree.MathNode("mstyle", [text], ["oldstylenums"]);
7699
- node = new mathMLTree.MathNode(tag, [ms]);
7700
- } else if (variant === "italic" || variant === "bold-italic") {
7707
+ if (variant === "italic" || variant === "bold-italic") {
7701
7708
  return italicNumber(text, variant, tag)
7702
7709
  } else {
7703
7710
  if (variant !== "normal") {
@@ -8888,7 +8895,7 @@ defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{
8888
8895
  defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}");
8889
8896
 
8890
8897
  /* eslint-disable */
8891
- /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8898
+ /* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8892
8899
  /* vim: set ts=2 et sw=2 tw=80: */
8893
8900
 
8894
8901
  /*************************************************************
@@ -10585,7 +10592,7 @@ defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap
10585
10592
  };
10586
10593
 
10587
10594
  //
10588
- // Helpers for code anaylsis
10595
+ // Helpers for code analysis
10589
10596
  // Will show type error at calling position
10590
10597
  //
10591
10598
  /** @param {number} a */
@@ -11010,15 +11017,15 @@ class MacroExpander {
11010
11017
  * Expand the next token only once if possible.
11011
11018
  *
11012
11019
  * If the token is expanded, the resulting tokens will be pushed onto
11013
- * the stack in reverse order and will be returned as an array,
11014
- * also in reverse order.
11020
+ * the stack in reverse order, and the number of such tokens will be
11021
+ * returned. This number might be zero or positive.
11015
11022
  *
11016
- * If not, the next token will be returned without removing it
11017
- * from the stack. This case can be detected by a `Token` return value
11018
- * instead of an `Array` return value.
11023
+ * If not, the return value is `false`, and the next token remains at the
11024
+ * top of the stack.
11019
11025
  *
11020
11026
  * In either case, the next token will be on the top of the stack,
11021
- * or the stack will be empty.
11027
+ * or the stack will be empty (in case of empty expansion
11028
+ * and no other tokens).
11022
11029
  *
11023
11030
  * Used to implement `expandAfterFuture` and `expandNextToken`.
11024
11031
  *
@@ -11034,7 +11041,7 @@ class MacroExpander {
11034
11041
  throw new ParseError("Undefined control sequence: " + name);
11035
11042
  }
11036
11043
  this.pushToken(topToken);
11037
- return topToken;
11044
+ return false;
11038
11045
  }
11039
11046
  this.expansionCount++;
11040
11047
  if (this.expansionCount > this.settings.maxExpand) {
@@ -11068,7 +11075,7 @@ class MacroExpander {
11068
11075
  }
11069
11076
  // Concatenate expansion onto top of stack.
11070
11077
  this.pushTokens(tokens);
11071
- return tokens;
11078
+ return tokens.length;
11072
11079
  }
11073
11080
 
11074
11081
  /**
@@ -11087,14 +11094,13 @@ class MacroExpander {
11087
11094
  */
11088
11095
  expandNextToken() {
11089
11096
  for (;;) {
11090
- const expanded = this.expandOnce();
11091
- // expandOnce returns Token if and only if it's fully expanded.
11092
- if (expanded instanceof Token) {
11097
+ if (this.expandOnce() === false) { // fully expanded
11098
+ const token = this.stack.pop();
11093
11099
  // The token after \noexpand is interpreted as if its meaning were ‘\relax’
11094
- if (expanded.treatAsRelax) {
11095
- expanded.text = "\\relax";
11100
+ if (token.treatAsRelax) {
11101
+ token.text = "\\relax";
11096
11102
  }
11097
- return this.stack.pop(); // === expanded
11103
+ return token
11098
11104
  }
11099
11105
  }
11100
11106
 
@@ -11120,15 +11126,15 @@ class MacroExpander {
11120
11126
  const oldStackLength = this.stack.length;
11121
11127
  this.pushTokens(tokens);
11122
11128
  while (this.stack.length > oldStackLength) {
11123
- const expanded = this.expandOnce(true); // expand only expandable tokens
11124
- // expandOnce returns Token if and only if it's fully expanded.
11125
- if (expanded instanceof Token) {
11126
- if (expanded.treatAsRelax) {
11129
+ // Expand only expandable tokens
11130
+ if (this.expandOnce(true) === false) { // fully expanded
11131
+ const token = this.stack.pop();
11132
+ if (token.treatAsRelax) {
11127
11133
  // the expansion of \noexpand is the token itself
11128
- expanded.noexpand = false;
11129
- expanded.treatAsRelax = false;
11134
+ token.noexpand = false;
11135
+ token.treatAsRelax = false;
11130
11136
  }
11131
- output.push(this.stack.pop());
11137
+ output.push(token);
11132
11138
  }
11133
11139
  }
11134
11140
  return output;
@@ -11428,6 +11434,36 @@ const uSubsAndSups = Object.freeze({
11428
11434
  '\u1DBF': 'θ'
11429
11435
  });
11430
11436
 
11437
+ // Used for Unicode input of calligraphic and script letters
11438
+ const asciiFromScript = Object.freeze({
11439
+ "\ud835\udc9c": "A",
11440
+ "\u212c": "B",
11441
+ "\ud835\udc9e": "C",
11442
+ "\ud835\udc9f": "D",
11443
+ "\u2130": "E",
11444
+ "\u2131": "F",
11445
+ "\ud835\udca2": "G",
11446
+ "\u210B": "H",
11447
+ "\u2110": "I",
11448
+ "\ud835\udca5": "J",
11449
+ "\ud835\udca6": "K",
11450
+ "\u2112": "L",
11451
+ "\u2113": "M",
11452
+ "\ud835\udca9": "N",
11453
+ "\ud835\udcaa": "O",
11454
+ "\ud835\udcab": "P",
11455
+ "\ud835\udcac": "Q",
11456
+ "\u211B": "R",
11457
+ "\ud835\udcae": "S",
11458
+ "\ud835\udcaf": "T",
11459
+ "\ud835\udcb0": "U",
11460
+ "\ud835\udcb1": "V",
11461
+ "\ud835\udcb2": "W",
11462
+ "\ud835\udcb3": "X",
11463
+ "\ud835\udcb4": "Y",
11464
+ "\ud835\udcb5": "Z"
11465
+ });
11466
+
11431
11467
  // Mapping of Unicode accent characters to their LaTeX equivalent in text and
11432
11468
  // math mode (when they exist).
11433
11469
  var unicodeAccents = {
@@ -11926,7 +11962,7 @@ class Parser {
11926
11962
  * Parses an "expression", which is a list of atoms.
11927
11963
  *
11928
11964
  * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
11929
- * happens when functions have higher precendence han infix
11965
+ * happens when functions have higher precedence han infix
11930
11966
  * nodes in implicit parses.
11931
11967
  *
11932
11968
  * `breakOnTokenText`: The text of the token that the expression should end
@@ -12177,12 +12213,16 @@ class Parser {
12177
12213
  return base
12178
12214
  } else {
12179
12215
  // We got either a superscript or subscript, create a supsub
12216
+ const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
12217
+ ? undefined
12218
+ : isDelimiter(this.nextToken.text);
12180
12219
  return {
12181
12220
  type: "supsub",
12182
12221
  mode: this.mode,
12183
12222
  base: base,
12184
12223
  sup: superscript,
12185
- sub: subscript
12224
+ sub: subscript,
12225
+ isFollowedByDelimiter
12186
12226
  }
12187
12227
  }
12188
12228
  } else {
@@ -12343,7 +12383,7 @@ class Parser {
12343
12383
  while (true) {
12344
12384
  const ch = this.fetch().text;
12345
12385
  // \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
12346
- if (ch === " " || ch === "\ufe0e") {
12386
+ if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
12347
12387
  this.consume();
12348
12388
  } else {
12349
12389
  break
@@ -12663,6 +12703,22 @@ class Parser {
12663
12703
  text
12664
12704
  };
12665
12705
  } else {
12706
+ if (asciiFromScript[text]) {
12707
+ // Unicode 14 disambiguates chancery from roundhand.
12708
+ // See https://www.unicode.org/charts/PDF/U1D400.pdf
12709
+ this.consume();
12710
+ const nextCode = this.fetch().text.charCodeAt(0);
12711
+ // mathcal is Temml default. Use mathscript if called for.
12712
+ const font = nextCode === 0xfe01 ? "mathscr" : "mathcal";
12713
+ if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); }
12714
+ return {
12715
+ type: "font",
12716
+ mode: "math",
12717
+ font,
12718
+ body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] }
12719
+ }
12720
+ }
12721
+ // Default ord character. No disambiguation necessary.
12666
12722
  s = {
12667
12723
  type: group,
12668
12724
  mode: this.mode,
@@ -12919,7 +12975,7 @@ class Style {
12919
12975
  * https://mit-license.org/
12920
12976
  */
12921
12977
 
12922
- const version = "0.9.2";
12978
+ const version = "0.10.2";
12923
12979
 
12924
12980
  function postProcess(block) {
12925
12981
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.9.2";
17
+ const version = "0.10.2";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.9.2",
3
+ "version": "0.10.2",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "homepage": "https://temml.org",
@@ -8,6 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "git://github.com/ronkok/Temml"
10
10
  },
11
+ "packageManager": "yarn@3.3.1",
11
12
  "files": [
12
13
  "temml.js",
13
14
  "src/",
@@ -230,15 +230,15 @@ export default class MacroExpander {
230
230
  * Expand the next token only once if possible.
231
231
  *
232
232
  * If the token is expanded, the resulting tokens will be pushed onto
233
- * the stack in reverse order and will be returned as an array,
234
- * also in reverse order.
233
+ * the stack in reverse order, and the number of such tokens will be
234
+ * returned. This number might be zero or positive.
235
235
  *
236
- * If not, the next token will be returned without removing it
237
- * from the stack. This case can be detected by a `Token` return value
238
- * instead of an `Array` return value.
236
+ * If not, the return value is `false`, and the next token remains at the
237
+ * top of the stack.
239
238
  *
240
239
  * In either case, the next token will be on the top of the stack,
241
- * or the stack will be empty.
240
+ * or the stack will be empty (in case of empty expansion
241
+ * and no other tokens).
242
242
  *
243
243
  * Used to implement `expandAfterFuture` and `expandNextToken`.
244
244
  *
@@ -254,7 +254,7 @@ export default class MacroExpander {
254
254
  throw new ParseError("Undefined control sequence: " + name);
255
255
  }
256
256
  this.pushToken(topToken);
257
- return topToken;
257
+ return false;
258
258
  }
259
259
  this.expansionCount++;
260
260
  if (this.expansionCount > this.settings.maxExpand) {
@@ -288,7 +288,7 @@ export default class MacroExpander {
288
288
  }
289
289
  // Concatenate expansion onto top of stack.
290
290
  this.pushTokens(tokens);
291
- return tokens;
291
+ return tokens.length;
292
292
  }
293
293
 
294
294
  /**
@@ -307,14 +307,13 @@ export default class MacroExpander {
307
307
  */
308
308
  expandNextToken() {
309
309
  for (;;) {
310
- const expanded = this.expandOnce();
311
- // expandOnce returns Token if and only if it's fully expanded.
312
- if (expanded instanceof Token) {
310
+ if (this.expandOnce() === false) { // fully expanded
311
+ const token = this.stack.pop();
313
312
  // The token after \noexpand is interpreted as if its meaning were ‘\relax’
314
- if (expanded.treatAsRelax) {
315
- expanded.text = "\\relax"
313
+ if (token.treatAsRelax) {
314
+ token.text = "\\relax"
316
315
  }
317
- return this.stack.pop(); // === expanded
316
+ return token
318
317
  }
319
318
  }
320
319
 
@@ -340,15 +339,15 @@ export default class MacroExpander {
340
339
  const oldStackLength = this.stack.length;
341
340
  this.pushTokens(tokens);
342
341
  while (this.stack.length > oldStackLength) {
343
- const expanded = this.expandOnce(true); // expand only expandable tokens
344
- // expandOnce returns Token if and only if it's fully expanded.
345
- if (expanded instanceof Token) {
346
- if (expanded.treatAsRelax) {
342
+ // Expand only expandable tokens
343
+ if (this.expandOnce(true) === false) { // fully expanded
344
+ const token = this.stack.pop();
345
+ if (token.treatAsRelax) {
347
346
  // the expansion of \noexpand is the token itself
348
- expanded.noexpand = false;
349
- expanded.treatAsRelax = false;
347
+ token.noexpand = false;
348
+ token.treatAsRelax = false;
350
349
  }
351
- output.push(this.stack.pop());
350
+ output.push(token);
352
351
  }
353
352
  }
354
353
  return output;
package/src/Parser.js CHANGED
@@ -7,8 +7,10 @@ import { supportedCodepoint } from "./unicodeScripts";
7
7
  import ParseError from "./ParseError";
8
8
  import { combiningDiacriticalMarksEndRegex } from "./Lexer";
9
9
  import { uSubsAndSups, unicodeSubRegEx } from "./unicodeSupOrSub"
10
+ import { asciiFromScript } from "./asciiFromScript"
10
11
  import SourceLocation from "./SourceLocation";
11
12
  import { Token } from "./Token";
13
+ import { isDelimiter } from "./functions/delimsizing"
12
14
 
13
15
  // Pre-evaluate both modules as unicodeSymbols require String.normalize()
14
16
  import unicodeAccents from /*preval*/ "./unicodeAccents";
@@ -172,7 +174,7 @@ export default class Parser {
172
174
  * Parses an "expression", which is a list of atoms.
173
175
  *
174
176
  * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
175
- * happens when functions have higher precendence han infix
177
+ * happens when functions have higher precedence han infix
176
178
  * nodes in implicit parses.
177
179
  *
178
180
  * `breakOnTokenText`: The text of the token that the expression should end
@@ -423,12 +425,16 @@ export default class Parser {
423
425
  return base
424
426
  } else {
425
427
  // We got either a superscript or subscript, create a supsub
428
+ const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
429
+ ? undefined
430
+ : isDelimiter(this.nextToken.text);
426
431
  return {
427
432
  type: "supsub",
428
433
  mode: this.mode,
429
434
  base: base,
430
435
  sup: superscript,
431
- sub: subscript
436
+ sub: subscript,
437
+ isFollowedByDelimiter
432
438
  }
433
439
  }
434
440
  } else {
@@ -589,7 +595,7 @@ export default class Parser {
589
595
  while (true) {
590
596
  const ch = this.fetch().text
591
597
  // \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
592
- if (ch === " " || ch === "\ufe0e") {
598
+ if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
593
599
  this.consume()
594
600
  } else {
595
601
  break
@@ -909,6 +915,22 @@ export default class Parser {
909
915
  text
910
916
  };
911
917
  } else {
918
+ if (asciiFromScript[text]) {
919
+ // Unicode 14 disambiguates chancery from roundhand.
920
+ // See https://www.unicode.org/charts/PDF/U1D400.pdf
921
+ this.consume()
922
+ const nextCode = this.fetch().text.charCodeAt(0)
923
+ // mathcal is Temml default. Use mathscript if called for.
924
+ const font = nextCode === 0xfe01 ? "mathscr" : "mathcal";
925
+ if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume() }
926
+ return {
927
+ type: "font",
928
+ mode: "math",
929
+ font,
930
+ body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] }
931
+ }
932
+ }
933
+ // Default ord character. No disambiguation necessary.
912
934
  s = {
913
935
  type: group,
914
936
  mode: this.mode,
@@ -0,0 +1,29 @@
1
+ // Used for Unicode input of calligraphic and script letters
2
+ export const asciiFromScript = Object.freeze({
3
+ "\ud835\udc9c": "A",
4
+ "\u212c": "B",
5
+ "\ud835\udc9e": "C",
6
+ "\ud835\udc9f": "D",
7
+ "\u2130": "E",
8
+ "\u2131": "F",
9
+ "\ud835\udca2": "G",
10
+ "\u210B": "H",
11
+ "\u2110": "I",
12
+ "\ud835\udca5": "J",
13
+ "\ud835\udca6": "K",
14
+ "\u2112": "L",
15
+ "\u2113": "M",
16
+ "\ud835\udca9": "N",
17
+ "\ud835\udcaa": "O",
18
+ "\ud835\udcab": "P",
19
+ "\ud835\udcac": "Q",
20
+ "\u211B": "R",
21
+ "\ud835\udcae": "S",
22
+ "\ud835\udcaf": "T",
23
+ "\ud835\udcb0": "U",
24
+ "\ud835\udcb1": "V",
25
+ "\ud835\udcb2": "W",
26
+ "\ud835\udcb3": "X",
27
+ "\ud835\udcb4": "Y",
28
+ "\ud835\udcb5": "Z"
29
+ })
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file converts a parse tree into a cooresponding MathML tree. The main
2
+ * This file converts a parse tree into a corresponding MathML tree. The main
3
3
  * entry point is the `buildMathML` function, which takes a parse tree from the
4
4
  * parser.
5
5
  */
@@ -11,13 +11,12 @@ defineFunction({
11
11
  names: ["\\\\"],
12
12
  props: {
13
13
  numArgs: 0,
14
- numOptionalArgs: 1,
15
- argTypes: ["size"],
14
+ numOptionalArgs: 0,
16
15
  allowedInText: true
17
16
  },
18
17
 
19
18
  handler({ parser }, args, optArgs) {
20
- const size = optArgs[0];
19
+ const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null;
21
20
  const newLine = !parser.settings.displayMode;
22
21
  return {
23
22
  type: "cr",
@@ -89,6 +89,11 @@ export const delimiters = [
89
89
  "."
90
90
  ];
91
91
 
92
+ // Export isDelimiter for benefit of parser.
93
+ const dels = ["}", "\\left", "\\middle", "\\right"]
94
+ export const isDelimiter = str => str.length > 0 &&
95
+ (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
96
+
92
97
  // Metrics of the different sizes. Found by looking at TeX's output of
93
98
  // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
94
99
  // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
@@ -67,7 +67,6 @@ defineFunction({
67
67
  "\\mathscr",
68
68
  "\\mathsf",
69
69
  "\\mathtt",
70
- "\\oldstylenums",
71
70
 
72
71
  // aliases
73
72
  "\\Bbb",