tex2typst 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,16 @@
1
1
  // src/map.ts
2
2
  var symbolMap = /* @__PURE__ */ new Map([
3
+ ["cos", "cos"],
4
+ ["sin", "sin"],
5
+ ["tan", "tan"],
6
+ ["cot", "cot"],
7
+ ["sec", "sec"],
8
+ ["csc", "csc"],
9
+ ["mod", "mod"],
10
+ ["omicron", "omicron"],
11
+ ["Xi", "Xi"],
12
+ ["Upsilon", "Upsilon"],
13
+ ["lim", "lim"],
3
14
  ["nonumber", ""],
4
15
  ["vec", "arrow"],
5
16
  ["neq", "eq.not"],
@@ -59,44 +70,16 @@ var symbolMap = /* @__PURE__ */ new Map([
59
70
  ["propto", "prop"],
60
71
  /* arrows */
61
72
  ["gets", "arrow.l"],
62
- ["hookleftarrow", "arrow.l.hook"],
63
- ["leftharpoonup", "harpoon.lt"],
64
- ["leftharpoondown", "harpoon.lb"],
65
- ["rightleftharpoons", "harpoons.rtlb"],
66
- ["longleftarrow", "arrow.l.long"],
67
- ["longrightarrow", "arrow.r.long"],
68
- ["longleftrightarrow", "arrow.l.r.long"],
69
- ["Longleftarrow", "arrow.l.double.long"],
70
- ["Longrightarrow", "arrow.r.double.long"],
71
- ["Longleftrightarrow", "arrow.l.r.double.long"],
72
73
  // ['longmapsto', 'arrow.r.bar'],
73
- ["hookrightarrow", "arrow.r.hook"],
74
- ["rightharpoonup", "harpoon.rt"],
75
- ["rightharpoondown", "harpoon.rb"],
76
74
  ["iff", "arrow.l.r.double.long"],
77
75
  ["implies", "arrow.r.double.long"],
78
- ["uparrow", "arrow.t"],
79
- ["downarrow", "arrow.b"],
80
- ["updownarrow", "arrow.t.b"],
81
- ["Uparrow", "arrow.t.double"],
82
- ["Downarrow", "arrow.b.double"],
83
- ["Updownarrow", "arrow.t.b.double"],
84
- ["nearrow", "arrow.tr"],
85
- ["searrow", "arrow.br"],
86
- ["swarrow", "arrow.bl"],
87
- ["nwarrow", "arrow.tl"],
88
76
  ["leadsto", "arrow.squiggly"],
89
- ["leftleftarrows", "arrows.ll"],
90
- ["rightrightarrows", "arrows.rr"],
91
77
  ["Cap", "sect.double"],
92
78
  ["Cup", "union.double"],
93
79
  ["Delta", "Delta"],
94
80
  ["Gamma", "Gamma"],
95
81
  ["Join", "join"],
96
82
  ["Lambda", "Lambda"],
97
- ["Leftarrow", "arrow.l.double"],
98
- ["Leftrightarrow", "arrow.l.r.double"],
99
- ["Longrightarrow", "arrow.r.double.long"],
100
83
  ["Omega", "Omega"],
101
84
  ["P", "pilcrow"],
102
85
  ["Phi", "Phi"],
@@ -152,7 +135,6 @@ var symbolMap = /* @__PURE__ */ new Map([
152
135
  ["div", "div"],
153
136
  ["divideontimes", "times.div"],
154
137
  ["dotplus", "plus.dot"],
155
- ["downarrow", "arrow.b"],
156
138
  ["ell", "ell"],
157
139
  ["emptyset", "nothing"],
158
140
  ["epsilon", "epsilon.alt"],
@@ -186,8 +168,6 @@ var symbolMap = /* @__PURE__ */ new Map([
186
168
  ["lbrack", "bracket.l"],
187
169
  ["ldots", "dots.h"],
188
170
  ["le", "lt.eq"],
189
- ["leadsto", "arrow.squiggly"],
190
- ["leftarrow", "arrow.l"],
191
171
  ["leftthreetimes", "times.three.l"],
192
172
  ["leftrightarrow", "arrow.l.r"],
193
173
  ["leq", "lt.eq"],
@@ -224,7 +204,6 @@ var symbolMap = /* @__PURE__ */ new Map([
224
204
  ["nu", "nu"],
225
205
  ["ntriangleleft", "lt.tri.not"],
226
206
  ["ntriangleright", "gt.tri.not"],
227
- ["nwarrow", "arrow.tl"],
228
207
  ["odot", "dot.circle"],
229
208
  ["oint", "integral.cont"],
230
209
  ["oiint", "integral.surf"],
@@ -277,7 +256,6 @@ var symbolMap = /* @__PURE__ */ new Map([
277
256
  ["supset", "supset"],
278
257
  ["supseteq", "supset.eq"],
279
258
  ["supsetneq", "supset.neq"],
280
- ["swarrow", "arrow.bl"],
281
259
  ["tau", "tau"],
282
260
  ["theta", "theta"],
283
261
  ["times", "times"],
@@ -288,8 +266,6 @@ var symbolMap = /* @__PURE__ */ new Map([
288
266
  // ['triangleleft', 'triangle.l.small'],
289
267
  // ['triangleright', 'triangle.r.small'],
290
268
  ["twoheadrightarrow", "arrow.r.twohead"],
291
- ["uparrow", "arrow.t"],
292
- ["updownarrow", "arrow.t.b"],
293
269
  ["upharpoonright", "harpoon.tr"],
294
270
  ["uplus", "union.plus"],
295
271
  ["upsilon", "upsilon"],
@@ -1081,6 +1057,7 @@ for (const [key, value] of Array.from(symbolMap.entries()).reverse()) {
1081
1057
  reverseSymbolMap.set(value, key);
1082
1058
  }
1083
1059
  reverseSymbolMap.set("dif", "mathrm{d}");
1060
+ reverseSymbolMap.set("oo", "infty");
1084
1061
  var typst_to_tex_map = /* @__PURE__ */ new Map([
1085
1062
  ["top", "top"],
1086
1063
  ["frac", "frac"],
@@ -1405,15 +1382,248 @@ var TypstNode = class {
1405
1382
  function isalpha(char) {
1406
1383
  return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(char);
1407
1384
  }
1408
- function isdigit(char) {
1409
- return "0123456789".includes(char);
1410
- }
1411
1385
  function assert(condition, message = "") {
1412
1386
  if (!condition) {
1413
1387
  throw new Error(message);
1414
1388
  }
1415
1389
  }
1416
1390
 
1391
+ // src/jslex.ts
1392
+ var EOF = {};
1393
+ function matchcompare(m1, m2) {
1394
+ if (m2.len !== m1.len) {
1395
+ return m2.len - m1.len;
1396
+ } else {
1397
+ return m1.index - m2.index;
1398
+ }
1399
+ }
1400
+ var Scanner = class {
1401
+ constructor(input, lexer) {
1402
+ // position within input stream
1403
+ this._pos = 0;
1404
+ // current line number
1405
+ this._line = 0;
1406
+ // current column number
1407
+ this._col = 0;
1408
+ this._offset = 0;
1409
+ this._less = null;
1410
+ this._go = false;
1411
+ this._newstate = null;
1412
+ this._text = null;
1413
+ this._leng = null;
1414
+ this._input = input;
1415
+ this._lexer = lexer;
1416
+ this._state = lexer.states[0];
1417
+ }
1418
+ /**
1419
+ * Analogous to yytext and yyleng in lex - will be set during scan.
1420
+ */
1421
+ text() {
1422
+ return this._text;
1423
+ }
1424
+ leng() {
1425
+ return this._leng;
1426
+ }
1427
+ /**
1428
+ * Position of in stream, line number and column number of match.
1429
+ */
1430
+ pos() {
1431
+ return this._pos;
1432
+ }
1433
+ line() {
1434
+ return this._line;
1435
+ }
1436
+ column() {
1437
+ return this._col;
1438
+ }
1439
+ /**
1440
+ * Analogous to input() in lex.
1441
+ * @return {string} The next character in the stream.
1442
+ */
1443
+ input() {
1444
+ return this._input.charAt(this._pos + this._leng + this._offset++);
1445
+ }
1446
+ /**
1447
+ * Similar to unput() in lex, but does not allow modifying the stream.
1448
+ * @return {int} The offset position after the operation.
1449
+ */
1450
+ unput() {
1451
+ return this._offset = this._offset > 0 ? this._offset-- : 0;
1452
+ }
1453
+ /**
1454
+ * Analogous to yyless(n) in lex - retains the first n characters from this pattern, and returns
1455
+ * the rest to the input stream, such that they will be used in the next pattern-matching operation.
1456
+ * @param {int} n Number of characters to retain.
1457
+ * @return {int} Length of the stream after the operation has completed.
1458
+ */
1459
+ less(n) {
1460
+ this._less = n;
1461
+ this._offset = 0;
1462
+ this._text = this._text.substring(0, n);
1463
+ return this._leng = this._text.length;
1464
+ }
1465
+ /**
1466
+ * Like less(), but instead of retaining the first n characters, it chops off the last n.
1467
+ * @param {int} n Number of characters to chop.
1468
+ * @return {int} Length of the stream after the operation has completed.
1469
+ */
1470
+ pushback(n) {
1471
+ return this.less(this._leng - n);
1472
+ }
1473
+ /**
1474
+ * Similar to REJECT in lex, except it doesn't break the current execution context.
1475
+ * TIP: reject() should be the last instruction in a spec callback.
1476
+ */
1477
+ reject() {
1478
+ this._go = true;
1479
+ }
1480
+ /**
1481
+ * Analogous to BEGIN in lex - sets the named state (start condition).
1482
+ * @param {string|int} state Name of state to switch to, or ordinal number (0 is first, etc).
1483
+ * @return {string} The new state on successful switch, throws exception on failure.
1484
+ */
1485
+ begin(state) {
1486
+ if (this._lexer.specification[state]) {
1487
+ return this._newstate = state;
1488
+ }
1489
+ const s = this._lexer.states[parseInt(state)];
1490
+ if (s) {
1491
+ return this._newstate = s;
1492
+ }
1493
+ throw "Unknown state '" + state + "' requested";
1494
+ }
1495
+ /**
1496
+ * Simple accessor for reading in the current state.
1497
+ * @return {string} The current state.
1498
+ */
1499
+ state() {
1500
+ return this._state;
1501
+ }
1502
+ /**
1503
+ * Scan method to be returned to caller - grabs the next token and fires appropriate calback.
1504
+ * @return {T} The next token extracted from the stream.
1505
+ */
1506
+ scan() {
1507
+ if (this._pos >= this._input.length) {
1508
+ return EOF;
1509
+ }
1510
+ const str = this._input.substring(this._pos);
1511
+ const rules = this._lexer.specification[this._state];
1512
+ const matches = [];
1513
+ for (let i = 0; i < rules.length; i++) {
1514
+ const rule = rules[i];
1515
+ const mt = str.match(rule.re);
1516
+ if (mt !== null && mt[0].length > 0) {
1517
+ matches.push({
1518
+ index: i,
1519
+ text: mt[0],
1520
+ len: mt[0].length,
1521
+ rule
1522
+ });
1523
+ }
1524
+ }
1525
+ if (matches.length === 0) {
1526
+ throw new Error("No match found for input '" + str + "'");
1527
+ }
1528
+ matches.sort(matchcompare);
1529
+ this._go = true;
1530
+ let result;
1531
+ let m;
1532
+ for (let j = 0, n = matches.length; j < n && this._go; j++) {
1533
+ this._offset = 0;
1534
+ this._less = null;
1535
+ this._go = false;
1536
+ this._newstate = null;
1537
+ m = matches[j];
1538
+ this._text = m.text;
1539
+ this._leng = m.len;
1540
+ result = m.rule.action(this);
1541
+ if (this._newstate && this._newstate != this._state) {
1542
+ this._state = this._newstate;
1543
+ break;
1544
+ }
1545
+ }
1546
+ const text = this._less === null ? m.text : m.text.substring(0, this._less);
1547
+ const len = text.length;
1548
+ this._pos += len + this._offset;
1549
+ const nlm = text.match(/\n/g);
1550
+ if (nlm !== null) {
1551
+ this._line += nlm.length;
1552
+ this._col = len - text.lastIndexOf("\n") - 1;
1553
+ } else {
1554
+ this._col += len;
1555
+ }
1556
+ return result;
1557
+ }
1558
+ };
1559
+ var JSLex = class {
1560
+ constructor(spec3) {
1561
+ this.states = Object.keys(spec3);
1562
+ this.specification = {};
1563
+ for (const s of this.states) {
1564
+ const rule_map = spec3[s];
1565
+ if (s in this.specification) {
1566
+ throw "Duplicate state declaration encountered for state '" + s + "'";
1567
+ }
1568
+ this.specification[s] = [];
1569
+ for (const [k, v] of rule_map.entries()) {
1570
+ let re;
1571
+ try {
1572
+ re = new RegExp("^" + k);
1573
+ } catch (err) {
1574
+ throw "Invalid regexp '" + k + "' in state '" + s + "' (" + err.message + ")";
1575
+ }
1576
+ this.specification[s].push({
1577
+ re,
1578
+ action: v
1579
+ });
1580
+ }
1581
+ }
1582
+ }
1583
+ /**
1584
+ * Scanner function - makes a new scanner object which is used to get tokens one at a time.
1585
+ * @param {string} input Input text to tokenize.
1586
+ * @return {function} Scanner function.
1587
+ */
1588
+ scanner(input) {
1589
+ return new Scanner(input, this);
1590
+ }
1591
+ /**
1592
+ * Similar to lex's yylex() function, consumes all input, calling calback for each token.
1593
+ * @param {string} input Text to lex.
1594
+ * @param {function} callback Function to execute for each token.
1595
+ */
1596
+ lex(input, callback) {
1597
+ const scanner = this.scanner(input);
1598
+ while (true) {
1599
+ const token = scanner.scan();
1600
+ if (token === EOF) {
1601
+ return;
1602
+ }
1603
+ if (token !== void 0) {
1604
+ callback(token);
1605
+ }
1606
+ }
1607
+ }
1608
+ /**
1609
+ * Consumes all input, collecting tokens along the way.
1610
+ * @param {string} input Text to lex.
1611
+ * @return {array} List of tokens, may contain an Error at the end.
1612
+ */
1613
+ collect(input) {
1614
+ const tokens = [];
1615
+ const callback = function(item) {
1616
+ if (Array.isArray(item)) {
1617
+ tokens.push(...item);
1618
+ } else {
1619
+ tokens.push(item);
1620
+ }
1621
+ };
1622
+ this.lex(input, callback);
1623
+ return tokens;
1624
+ }
1625
+ };
1626
+
1417
1627
  // src/tex-parser.ts
1418
1628
  var UNARY_COMMANDS = [
1419
1629
  "sqrt",
@@ -1443,7 +1653,9 @@ var UNARY_COMMANDS = [
1443
1653
  "underline",
1444
1654
  "vec",
1445
1655
  "widehat",
1446
- "widetilde"
1656
+ "widetilde",
1657
+ "overleftarrow",
1658
+ "overrightarrow"
1447
1659
  ];
1448
1660
  var BINARY_COMMANDS = [
1449
1661
  "frac",
@@ -1492,13 +1704,6 @@ function eat_primes(tokens, start) {
1492
1704
  }
1493
1705
  return pos - start;
1494
1706
  }
1495
- function eat_command_name(latex, start) {
1496
- let pos = start;
1497
- while (pos < latex.length && isalpha(latex[pos])) {
1498
- pos += 1;
1499
- }
1500
- return latex.substring(start, pos);
1501
- }
1502
1707
  function find_closing_match(tokens, start, leftToken, rightToken) {
1503
1708
  assert(tokens[start].eq(leftToken));
1504
1709
  let count = 1;
@@ -1526,126 +1731,46 @@ var END_COMMAND = new TexToken(1 /* COMMAND */, "\\end");
1526
1731
  function find_closing_end_command(tokens, start) {
1527
1732
  return find_closing_match(tokens, start, BEGIN_COMMAND, END_COMMAND);
1528
1733
  }
1529
- function find_closing_curly_bracket_char(latex, start) {
1530
- assert(latex[start] === "{");
1531
- let count = 1;
1532
- let pos = start + 1;
1533
- while (count > 0) {
1534
- if (pos >= latex.length) {
1535
- throw new LatexParserError("Unmatched curly brackets");
1536
- }
1537
- if (pos + 1 < latex.length && ["\\{", "\\}"].includes(latex.substring(pos, pos + 2))) {
1538
- pos += 2;
1539
- continue;
1540
- }
1541
- if (latex[pos] === "{") {
1542
- count += 1;
1543
- } else if (latex[pos] === "}") {
1544
- count -= 1;
1545
- }
1546
- pos += 1;
1734
+ function unescape(str) {
1735
+ const chars = ["{", "}", "\\", "$", "&", "#", "_", "%"];
1736
+ for (const char of chars) {
1737
+ str = str.replaceAll("\\" + char, char);
1547
1738
  }
1548
- return pos - 1;
1739
+ return str;
1549
1740
  }
1550
- function tokenize(latex) {
1551
- const tokens = [];
1552
- let pos = 0;
1553
- while (pos < latex.length) {
1554
- const firstChar = latex[pos];
1555
- let token;
1556
- switch (firstChar) {
1557
- case "%": {
1558
- let newPos = pos + 1;
1559
- while (newPos < latex.length && latex[newPos] !== "\n") {
1560
- newPos += 1;
1561
- }
1562
- token = new TexToken(3 /* COMMENT */, latex.slice(pos + 1, newPos));
1563
- pos = newPos;
1564
- break;
1565
- }
1566
- case "{":
1567
- case "}":
1568
- case "_":
1569
- case "^":
1570
- case "&":
1571
- token = new TexToken(6 /* CONTROL */, firstChar);
1572
- pos++;
1573
- break;
1574
- case "\n":
1575
- token = new TexToken(5 /* NEWLINE */, firstChar);
1576
- pos++;
1577
- break;
1578
- case "\r": {
1579
- if (pos + 1 < latex.length && latex[pos + 1] === "\n") {
1580
- token = new TexToken(5 /* NEWLINE */, "\n");
1581
- pos += 2;
1582
- } else {
1583
- token = new TexToken(5 /* NEWLINE */, "\n");
1584
- pos++;
1585
- }
1586
- break;
1587
- }
1588
- case " ": {
1589
- let newPos = pos;
1590
- while (newPos < latex.length && latex[newPos] === " ") {
1591
- newPos += 1;
1592
- }
1593
- token = new TexToken(4 /* SPACE */, latex.slice(pos, newPos));
1594
- pos = newPos;
1595
- break;
1596
- }
1597
- case "\\": {
1598
- if (pos + 1 >= latex.length) {
1599
- throw new LatexParserError("Expecting command name after \\");
1600
- }
1601
- const firstTwoChars = latex.slice(pos, pos + 2);
1602
- if (["\\\\", "\\,"].includes(firstTwoChars)) {
1603
- token = new TexToken(6 /* CONTROL */, firstTwoChars);
1604
- } else if (["\\{", "\\}", "\\%", "\\$", "\\&", "\\#", "\\_", "\\|"].includes(firstTwoChars)) {
1605
- token = new TexToken(0 /* ELEMENT */, firstTwoChars);
1606
- } else {
1607
- const command = eat_command_name(latex, pos + 1);
1608
- token = new TexToken(1 /* COMMAND */, "\\" + command);
1609
- }
1610
- pos += token.value.length;
1611
- break;
1612
- }
1613
- default: {
1614
- if (isdigit(firstChar)) {
1615
- let newPos = pos;
1616
- while (newPos < latex.length && isdigit(latex[newPos])) {
1617
- newPos += 1;
1618
- }
1619
- token = new TexToken(0 /* ELEMENT */, latex.slice(pos, newPos));
1620
- } else if (isalpha(firstChar)) {
1621
- token = new TexToken(0 /* ELEMENT */, firstChar);
1622
- } else if ("+-*/='<>!.,;:?()[]|".includes(firstChar)) {
1623
- token = new TexToken(0 /* ELEMENT */, firstChar);
1624
- } else {
1625
- token = new TexToken(7 /* UNKNOWN */, firstChar);
1626
- }
1627
- pos += token.value.length;
1628
- }
1629
- }
1630
- tokens.push(token);
1631
- if (token.type === 1 /* COMMAND */ && ["\\text", "\\operatorname", "\\begin", "\\end"].includes(token.value)) {
1632
- if (pos >= latex.length || latex[pos] !== "{") {
1633
- throw new LatexParserError(`No content for ${token.value} command`);
1634
- }
1635
- tokens.push(new TexToken(6 /* CONTROL */, "{"));
1636
- const posClosingBracket = find_closing_curly_bracket_char(latex, pos);
1637
- pos++;
1638
- let textInside = latex.slice(pos, posClosingBracket);
1639
- const chars = ["{", "}", "\\", "$", "&", "#", "_", "%"];
1640
- for (const char of chars) {
1641
- textInside = textInside.replaceAll("\\" + char, char);
1642
- }
1643
- tokens.push(new TexToken(2 /* TEXT */, textInside));
1644
- tokens.push(new TexToken(6 /* CONTROL */, "}"));
1645
- pos = posClosingBracket + 1;
1646
- }
1647
- }
1648
- return tokens;
1741
+ var rules_map = /* @__PURE__ */ new Map([
1742
+ [
1743
+ String.raw`\\(text|operatorname|begin|end){.+?}`,
1744
+ (s) => {
1745
+ const text = s.text();
1746
+ const command = text.substring(0, text.indexOf("{"));
1747
+ const text_inside = text.substring(text.indexOf("{") + 1, text.lastIndexOf("}"));
1748
+ return [
1749
+ new TexToken(1 /* COMMAND */, command),
1750
+ new TexToken(6 /* CONTROL */, "{"),
1751
+ new TexToken(2 /* TEXT */, unescape(text_inside)),
1752
+ new TexToken(6 /* CONTROL */, "}")
1753
+ ];
1754
+ }
1755
+ ],
1756
+ [String.raw`%[^\n]*`, (s) => new TexToken(3 /* COMMENT */, s.text().substring(1))],
1757
+ [String.raw`[{}_^&]`, (s) => new TexToken(6 /* CONTROL */, s.text())],
1758
+ [String.raw`\r?\n`, (_s) => new TexToken(5 /* NEWLINE */, "\n")],
1759
+ [String.raw`\s+`, (s) => new TexToken(4 /* SPACE */, s.text())],
1760
+ [String.raw`\\[\\,]`, (s) => new TexToken(6 /* CONTROL */, s.text())],
1761
+ [String.raw`\\[{}%$&#_|]`, (s) => new TexToken(0 /* ELEMENT */, s.text())],
1762
+ [String.raw`\\[a-zA-Z]+`, (s) => new TexToken(1 /* COMMAND */, s.text())],
1763
+ [String.raw`[0-9]+`, (s) => new TexToken(0 /* ELEMENT */, s.text())],
1764
+ [String.raw`[a-zA-Z]`, (s) => new TexToken(0 /* ELEMENT */, s.text())],
1765
+ [String.raw`[+\-*/='<>!.,;:?()\[\]|]`, (s) => new TexToken(0 /* ELEMENT */, s.text())],
1766
+ [String.raw`.`, (s) => new TexToken(7 /* UNKNOWN */, s.text())]
1767
+ ]);
1768
+ var spec = {
1769
+ "start": rules_map
1770
+ };
1771
+ function tokenize_tex(input) {
1772
+ const lexer = new JSLex(spec);
1773
+ return lexer.collect(input);
1649
1774
  }
1650
1775
  var LatexParserError = class extends Error {
1651
1776
  constructor(message) {
@@ -1961,7 +2086,7 @@ function passExpandCustomTexMacros(tokens, customTexMacros) {
1961
2086
  let out_tokens = [];
1962
2087
  for (const token of tokens) {
1963
2088
  if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
1964
- const expanded_tokens = tokenize(customTexMacros[token.value]);
2089
+ const expanded_tokens = tokenize_tex(customTexMacros[token.value]);
1965
2090
  out_tokens = out_tokens.concat(expanded_tokens);
1966
2091
  } else {
1967
2092
  out_tokens.push(token);
@@ -1971,23 +2096,61 @@ function passExpandCustomTexMacros(tokens, customTexMacros) {
1971
2096
  }
1972
2097
  function parseTex(tex, customTexMacros) {
1973
2098
  const parser = new LatexParser();
1974
- let tokens = tokenize(tex);
2099
+ let tokens = tokenize_tex(tex);
1975
2100
  tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
1976
2101
  tokens = passExpandCustomTexMacros(tokens, customTexMacros);
1977
2102
  return parser.parse(tokens);
1978
2103
  }
1979
2104
 
2105
+ // src/typst-shorthands.ts
2106
+ var shorthandMap = /* @__PURE__ */ new Map([
2107
+ ["arrow.l.r.double.long", "<==>"],
2108
+ ["arrow.l.r.long", "<-->"],
2109
+ ["arrow.r.bar", "|->"],
2110
+ ["arrow.r.double.bar", "|=>"],
2111
+ ["arrow.r.double.long", "==>"],
2112
+ ["arrow.r.long", "-->"],
2113
+ ["arrow.r.long.squiggly", "~~>"],
2114
+ ["arrow.r.tail", ">->"],
2115
+ ["arrow.r.twohead", "->>"],
2116
+ ["arrow.l.double.long", "<=="],
2117
+ ["arrow.l.long", "<--"],
2118
+ ["arrow.l.long.squiggly", "<~~"],
2119
+ ["arrow.l.tail", "<-<"],
2120
+ ["arrow.l.twohead", "<<-"],
2121
+ ["arrow.l.r", "<->"],
2122
+ ["arrow.l.r.double", "<=>"],
2123
+ ["colon.double.eq", "::="],
2124
+ ["dots.h", "..."],
2125
+ ["gt.triple", ">>>"],
2126
+ ["lt.triple", "<<<"],
2127
+ ["arrow.r", "->"],
2128
+ ["arrow.r.double", "=>"],
2129
+ ["arrow.r.squiggly", "~>"],
2130
+ ["arrow.l", "<-"],
2131
+ ["arrow.l.squiggly", "<~"],
2132
+ ["bar.v.double", "||"],
2133
+ ["bracket.l.double", "[|"],
2134
+ ["bracket.r.double", "|]"],
2135
+ ["colon.eq", ":="],
2136
+ ["eq.colon", "=:"],
2137
+ ["eq.not", "!="],
2138
+ ["gt.double", ">>"],
2139
+ ["gt.eq", ">="],
2140
+ ["lt.double", "<<"],
2141
+ ["lt.eq", "<="],
2142
+ ["ast.op", "*"],
2143
+ ["minus", "-"],
2144
+ ["tilde.op", "~"]
2145
+ ]);
2146
+ var reverseShorthandMap = /* @__PURE__ */ new Map();
2147
+ for (const [key, value] of shorthandMap.entries()) {
2148
+ if (value.length > 1) {
2149
+ reverseShorthandMap.set(value, key);
2150
+ }
2151
+ }
2152
+
1980
2153
  // src/typst-writer.ts
1981
- var TYPST_INTRINSIC_SYMBOLS = [
1982
- "dim",
1983
- "id",
1984
- "im",
1985
- "mod",
1986
- "Pr",
1987
- "sech",
1988
- "csch"
1989
- // 'sgn
1990
- ];
1991
2154
  function is_delimiter(c) {
1992
2155
  return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
1993
2156
  }
@@ -2006,6 +2169,8 @@ function typst_primitive_to_string(value) {
2006
2169
  default:
2007
2170
  if (value === null) {
2008
2171
  return "#none";
2172
+ } else if (value instanceof TypstToken) {
2173
+ return value.toString();
2009
2174
  }
2010
2175
  throw new TypstWriterError(`Invalid primitive value: ${value}`, value);
2011
2176
  }
@@ -2018,13 +2183,14 @@ var TypstWriterError = class extends Error {
2018
2183
  }
2019
2184
  };
2020
2185
  var TypstWriter = class {
2021
- constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
2186
+ constructor(opt) {
2022
2187
  this.buffer = "";
2023
2188
  this.queue = [];
2024
2189
  this.insideFunctionDepth = 0;
2025
- this.nonStrict = nonStrict;
2026
- this.preferTypstIntrinsic = preferTypstIntrinsic;
2027
- this.keepSpaces = keepSpaces;
2190
+ this.nonStrict = opt.nonStrict;
2191
+ this.preferShorthands = opt.preferShorthands;
2192
+ this.keepSpaces = opt.keepSpaces;
2193
+ this.inftyToOo = opt.inftyToOo;
2028
2194
  }
2029
2195
  writeBuffer(token) {
2030
2196
  const str = token.toString();
@@ -2063,9 +2229,19 @@ var TypstWriter = class {
2063
2229
  }
2064
2230
  break;
2065
2231
  }
2066
- case "symbol":
2067
- this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
2232
+ case "symbol": {
2233
+ let content = node.content;
2234
+ if (this.preferShorthands) {
2235
+ if (shorthandMap.has(content)) {
2236
+ content = shorthandMap.get(content);
2237
+ }
2238
+ }
2239
+ if (this.inftyToOo && content === "infinity") {
2240
+ content = "oo";
2241
+ }
2242
+ this.queue.push(new TypstToken(0 /* SYMBOL */, content));
2068
2243
  break;
2244
+ }
2069
2245
  case "text":
2070
2246
  this.queue.push(new TypstToken(2 /* TEXT */, node.content));
2071
2247
  break;
@@ -2267,6 +2443,16 @@ var TypstWriter = class {
2267
2443
  };
2268
2444
 
2269
2445
  // src/convert.ts
2446
+ var TYPST_INTRINSIC_SYMBOLS = [
2447
+ "dim",
2448
+ "id",
2449
+ "im",
2450
+ "mod",
2451
+ "Pr",
2452
+ "sech",
2453
+ "csch"
2454
+ // 'sgn
2455
+ ];
2270
2456
  function tex_token_to_typst(token) {
2271
2457
  if (/^[a-zA-Z0-9]$/.test(token)) {
2272
2458
  return token;
@@ -2448,6 +2634,20 @@ function convert_tex_node_to_typst(node, options = {}) {
2448
2634
  if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
2449
2635
  return new TypstNode("symbol", arg0.content + arg0.content);
2450
2636
  }
2637
+ if (node.content === "\\overrightarrow") {
2638
+ return new TypstNode(
2639
+ "funcCall",
2640
+ "arrow",
2641
+ [arg0]
2642
+ );
2643
+ }
2644
+ if (node.content === "\\overleftarrow") {
2645
+ return new TypstNode(
2646
+ "funcCall",
2647
+ "accent",
2648
+ [arg0, new TypstNode("symbol", "arrow.l")]
2649
+ );
2650
+ }
2451
2651
  if (node.content === "\\operatorname") {
2452
2652
  const body = node.args;
2453
2653
  if (body.length !== 1 || body[0].type !== "text") {
@@ -2495,14 +2695,8 @@ function convert_tex_node_to_typst(node, options = {}) {
2495
2695
  delim = "|";
2496
2696
  break;
2497
2697
  case "Vmatrix": {
2498
- const matrix2 = new TypstNode("matrix", "", [], data);
2499
- matrix2.setOptions({ "delim": TYPST_NONE });
2500
- const group = new TypstNode("group", "", [
2501
- new TypstNode("symbol", "||"),
2502
- matrix2,
2503
- new TypstNode("symbol", "||")
2504
- ]);
2505
- return new TypstNode("funcCall", "lr", [group]);
2698
+ delim = new TypstToken(0 /* SYMBOL */, "bar.v.double");
2699
+ break;
2506
2700
  }
2507
2701
  default:
2508
2702
  throw new TypstWriterError(`Unimplemented beginend: ${node.content}`, node);
@@ -2731,6 +2925,8 @@ function convert_typst_node_to_tex(node) {
2731
2925
  }
2732
2926
 
2733
2927
  // src/typst-parser.ts
2928
+ var TYPST_EMPTY_NODE = new TypstNode("empty", "");
2929
+ var TYPST_SHORTHANDS = Array.from(reverseShorthandMap.keys());
2734
2930
  function eat_primes2(tokens, start) {
2735
2931
  let pos = start;
2736
2932
  while (pos < tokens.length && tokens[pos].eq(new TypstToken(1 /* ELEMENT */, "'"))) {
@@ -2738,126 +2934,67 @@ function eat_primes2(tokens, start) {
2738
2934
  }
2739
2935
  return pos - start;
2740
2936
  }
2741
- function eat_identifier_name(typst, start) {
2742
- let pos = start;
2743
- while (pos < typst.length && (isalpha(typst[pos]) || typst[pos] === ".")) {
2744
- pos += 1;
2745
- }
2746
- return typst.substring(start, pos);
2937
+ function generate_regex_for_shorthands() {
2938
+ const regex_list = TYPST_SHORTHANDS.map((s) => {
2939
+ s = s.replaceAll("|", "\\|");
2940
+ s = s.replaceAll(".", "\\.");
2941
+ s = s.replaceAll("[", "\\[");
2942
+ s = s.replaceAll("]", "\\]");
2943
+ return s;
2944
+ });
2945
+ return `(${regex_list.join("|")})`;
2747
2946
  }
2748
- var TYPST_EMPTY_NODE = new TypstNode("empty", "");
2749
- function tokenize_typst(typst) {
2750
- const tokens = [];
2751
- let pos = 0;
2752
- while (pos < typst.length) {
2753
- const firstChar = typst[pos];
2754
- let token;
2755
- switch (firstChar) {
2756
- case "_":
2757
- case "^":
2758
- case "&":
2759
- token = new TypstToken(6 /* CONTROL */, firstChar);
2760
- pos++;
2761
- break;
2762
- case "\n":
2763
- token = new TypstToken(7 /* NEWLINE */, firstChar);
2764
- pos++;
2765
- break;
2766
- case "\r": {
2767
- if (pos + 1 < typst.length && typst[pos + 1] === "\n") {
2768
- token = new TypstToken(7 /* NEWLINE */, "\n");
2769
- pos += 2;
2770
- } else {
2771
- token = new TypstToken(7 /* NEWLINE */, "\n");
2772
- pos++;
2773
- }
2774
- break;
2775
- }
2776
- case " ": {
2777
- let newPos = pos;
2778
- while (newPos < typst.length && typst[newPos] === " ") {
2779
- newPos++;
2780
- }
2781
- token = new TypstToken(4 /* SPACE */, typst.substring(pos, newPos));
2782
- pos = newPos;
2783
- break;
2784
- }
2785
- case "/": {
2786
- if (pos < typst.length && typst[pos + 1] === "/") {
2787
- let newPos = pos + 2;
2788
- while (newPos < typst.length && typst[newPos] !== "\n") {
2789
- newPos++;
2790
- }
2791
- token = new TypstToken(3 /* COMMENT */, typst.slice(pos + 2, newPos));
2792
- pos = newPos;
2793
- } else {
2794
- token = new TypstToken(1 /* ELEMENT */, "/");
2795
- pos++;
2796
- }
2797
- break;
2798
- }
2799
- case "\\": {
2800
- if (pos + 1 >= typst.length) {
2801
- throw new Error("Expecting a character after \\");
2802
- }
2803
- const firstTwoChars = typst.substring(pos, pos + 2);
2804
- if (["\\$", "\\&", "\\#", "\\_"].includes(firstTwoChars)) {
2805
- token = new TypstToken(1 /* ELEMENT */, firstTwoChars);
2806
- pos += 2;
2807
- } else if (["\\\n", "\\ "].includes(firstTwoChars)) {
2808
- token = new TypstToken(6 /* CONTROL */, "\\");
2809
- pos += 1;
2810
- } else {
2811
- token = new TypstToken(6 /* CONTROL */, "");
2812
- pos++;
2813
- }
2814
- break;
2815
- }
2816
- case '"': {
2817
- let newPos = pos + 1;
2818
- while (newPos < typst.length) {
2819
- if (typst[newPos] === '"' && typst[newPos - 1] !== "\\") {
2820
- break;
2821
- }
2822
- newPos++;
2823
- }
2824
- let text = typst.substring(pos + 1, newPos);
2825
- const chars = ['"', "\\"];
2826
- for (const char of chars) {
2827
- text = text.replaceAll("\\" + char, char);
2828
- }
2829
- token = new TypstToken(2 /* TEXT */, text);
2830
- pos = newPos + 1;
2831
- break;
2832
- }
2833
- default: {
2834
- if (isdigit(firstChar)) {
2835
- let newPos = pos;
2836
- while (newPos < typst.length && isdigit(typst[newPos])) {
2837
- newPos += 1;
2838
- }
2839
- if (newPos < typst.length && typst[newPos] === ".") {
2840
- newPos += 1;
2841
- while (newPos < typst.length && isdigit(typst[newPos])) {
2842
- newPos += 1;
2843
- }
2844
- }
2845
- token = new TypstToken(1 /* ELEMENT */, typst.slice(pos, newPos));
2846
- } else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
2847
- token = new TypstToken(1 /* ELEMENT */, firstChar);
2848
- } else if (isalpha(firstChar)) {
2849
- const identifier = eat_identifier_name(typst, pos);
2850
- const _type = identifier.length === 1 ? 1 /* ELEMENT */ : 0 /* SYMBOL */;
2851
- token = new TypstToken(_type, identifier);
2852
- } else {
2853
- token = new TypstToken(1 /* ELEMENT */, firstChar);
2854
- }
2855
- pos += token.value.length;
2856
- }
2857
- }
2858
- tokens.push(token);
2859
- }
2860
- return tokens;
2947
+ var REGEX_SHORTHANDS = generate_regex_for_shorthands();
2948
+ var rules_map2 = /* @__PURE__ */ new Map([
2949
+ [String.raw`//[^\n]*`, (s) => new TypstToken(3 /* COMMENT */, s.text().substring(2))],
2950
+ [String.raw`/`, (s) => new TypstToken(1 /* ELEMENT */, s.text())],
2951
+ [String.raw`[_^&]`, (s) => new TypstToken(6 /* CONTROL */, s.text())],
2952
+ [String.raw`\r?\n`, (_s) => new TypstToken(7 /* NEWLINE */, "\n")],
2953
+ [String.raw`\s+`, (s) => new TypstToken(4 /* SPACE */, s.text())],
2954
+ [String.raw`\\[$&#_]`, (s) => new TypstToken(1 /* ELEMENT */, s.text())],
2955
+ [String.raw`\\\n`, (s) => {
2956
+ return [
2957
+ new TypstToken(6 /* CONTROL */, "\\"),
2958
+ new TypstToken(7 /* NEWLINE */, "\n")
2959
+ ];
2960
+ }],
2961
+ [String.raw`\\\s`, (s) => {
2962
+ return [
2963
+ new TypstToken(6 /* CONTROL */, "\\"),
2964
+ new TypstToken(4 /* SPACE */, " ")
2965
+ ];
2966
+ }],
2967
+ // this backslash is dummy and will be ignored in later stages
2968
+ [String.raw`\\\S`, (_s) => new TypstToken(6 /* CONTROL */, "")],
2969
+ [
2970
+ String.raw`"([^"]|(\\"))*"`,
2971
+ (s) => {
2972
+ const text = s.text().substring(1, s.text().length - 1);
2973
+ text.replaceAll('\\"', '"');
2974
+ return new TypstToken(2 /* TEXT */, text);
2975
+ }
2976
+ ],
2977
+ [
2978
+ REGEX_SHORTHANDS,
2979
+ (s) => {
2980
+ const shorthand = s.text();
2981
+ const symbol = reverseShorthandMap.get(shorthand);
2982
+ return new TypstToken(0 /* SYMBOL */, symbol);
2983
+ }
2984
+ ],
2985
+ [String.raw`[0-9]+(\.[0-9]+)?`, (s) => new TypstToken(1 /* ELEMENT */, s.text())],
2986
+ [String.raw`[+\-*/=\'<>!.,;?()\[\]|]`, (s) => new TypstToken(1 /* ELEMENT */, s.text())],
2987
+ [String.raw`[a-zA-Z\.]+`, (s) => {
2988
+ return new TypstToken(s.text().length === 1 ? 1 /* ELEMENT */ : 0 /* SYMBOL */, s.text());
2989
+ }],
2990
+ [String.raw`.`, (s) => new TypstToken(1 /* ELEMENT */, s.text())]
2991
+ ]);
2992
+ var spec2 = {
2993
+ "start": rules_map2
2994
+ };
2995
+ function tokenize_typst(input) {
2996
+ const lexer = new JSLex(spec2);
2997
+ return lexer.collect(input);
2861
2998
  }
2862
2999
  function find_closing_match2(tokens, start) {
2863
3000
  assert(tokens[start].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2]));
@@ -3279,27 +3416,22 @@ function tex2typst(tex, options) {
3279
3416
  const opt = {
3280
3417
  nonStrict: true,
3281
3418
  preferTypstIntrinsic: true,
3419
+ preferShorthands: true,
3282
3420
  keepSpaces: false,
3283
3421
  fracToSlash: true,
3422
+ inftyToOo: false,
3284
3423
  customTexMacros: {}
3285
3424
  };
3286
- if (options) {
3287
- if (options.nonStrict) {
3288
- opt.nonStrict = options.nonStrict;
3289
- }
3290
- if (options.preferTypstIntrinsic) {
3291
- opt.preferTypstIntrinsic = options.preferTypstIntrinsic;
3292
- }
3293
- if (options.customTexMacros) {
3294
- opt.customTexMacros = options.customTexMacros;
3295
- }
3296
- if (options.fracToSlash !== void 0) {
3297
- opt.fracToSlash = options.fracToSlash;
3425
+ if (options !== void 0) {
3426
+ for (const key in opt) {
3427
+ if (options[key] !== void 0) {
3428
+ opt[key] = options[key];
3429
+ }
3298
3430
  }
3299
3431
  }
3300
3432
  const texTree = parseTex(tex, opt.customTexMacros);
3301
3433
  const typstTree = convert_tex_node_to_typst(texTree, opt);
3302
- const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
3434
+ const writer = new TypstWriter(opt);
3303
3435
  writer.serialize(typstTree);
3304
3436
  return writer.finalize();
3305
3437
  }