tex2typst 0.3.0-alpha → 0.3.0-beta-1

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
@@ -42,7 +42,7 @@ var symbolMap = new Map([
42
42
  ["approx", "approx"],
43
43
  ["cong", "tilde.equiv"],
44
44
  ["simeq", "tilde.eq"],
45
- ["asymp", "\u224D"],
45
+ ["asymp", ""],
46
46
  ["equiv", "equiv"],
47
47
  ["propto", "prop"],
48
48
  ["gets", "arrow.l"],
@@ -1027,12 +1027,6 @@ function array_split(array, sep) {
1027
1027
  }
1028
1028
 
1029
1029
  // src/types.ts
1030
- function apply_escape_if_needed(c) {
1031
- if (["{", "}", "%"].includes(c)) {
1032
- return "\\" + c;
1033
- }
1034
- return c;
1035
- }
1036
1030
  class TexToken {
1037
1031
  type;
1038
1032
  value;
@@ -1054,6 +1048,12 @@ class TexToken {
1054
1048
  }
1055
1049
  }
1056
1050
  }
1051
+ function apply_escape_if_needed(c) {
1052
+ if (["{", "}", "%"].includes(c)) {
1053
+ return "\\" + c;
1054
+ }
1055
+ return c;
1056
+ }
1057
1057
 
1058
1058
  class TexNode {
1059
1059
  type;
@@ -1166,7 +1166,8 @@ class TexNode {
1166
1166
  let tokens = [];
1167
1167
  const matrix = this.data;
1168
1168
  tokens.push(new TexToken(1 /* COMMAND */, `\\begin{${this.content}}`));
1169
- tokens.push(new TexToken(5 /* NEWLINE */, "\n"));
1169
+ tokens.push(new TexToken(5 /* NEWLINE */, `
1170
+ `));
1170
1171
  for (let i = 0;i < matrix.length; i++) {
1171
1172
  const row = matrix[i];
1172
1173
  for (let j = 0;j < row.length; j++) {
@@ -1180,7 +1181,8 @@ class TexNode {
1180
1181
  tokens.push(new TexToken(6 /* CONTROL */, "\\\\"));
1181
1182
  }
1182
1183
  }
1183
- tokens.push(new TexToken(5 /* NEWLINE */, "\n"));
1184
+ tokens.push(new TexToken(5 /* NEWLINE */, `
1185
+ `));
1184
1186
  tokens.push(new TexToken(1 /* COMMAND */, `\\end{${this.content}}`));
1185
1187
  return tokens;
1186
1188
  }
@@ -1280,6 +1282,46 @@ function assert(condition, message = "") {
1280
1282
  }
1281
1283
 
1282
1284
  // src/tex-parser.ts
1285
+ var UNARY_COMMANDS = [
1286
+ "sqrt",
1287
+ "text",
1288
+ "bar",
1289
+ "bold",
1290
+ "boldsymbol",
1291
+ "ddot",
1292
+ "dot",
1293
+ "hat",
1294
+ "mathbb",
1295
+ "mathbf",
1296
+ "mathcal",
1297
+ "mathfrak",
1298
+ "mathit",
1299
+ "mathrm",
1300
+ "mathscr",
1301
+ "mathsf",
1302
+ "mathtt",
1303
+ "operatorname",
1304
+ "overbrace",
1305
+ "overline",
1306
+ "pmb",
1307
+ "rm",
1308
+ "tilde",
1309
+ "underbrace",
1310
+ "underline",
1311
+ "vec",
1312
+ "widehat",
1313
+ "widetilde"
1314
+ ];
1315
+ var BINARY_COMMANDS = [
1316
+ "frac",
1317
+ "tfrac",
1318
+ "binom",
1319
+ "dbinom",
1320
+ "dfrac",
1321
+ "tbinom",
1322
+ "overset"
1323
+ ];
1324
+ var EMPTY_NODE = new TexNode("empty", "");
1283
1325
  function get_command_param_num(command) {
1284
1326
  if (UNARY_COMMANDS.includes(command)) {
1285
1327
  return 1;
@@ -1289,6 +1331,10 @@ function get_command_param_num(command) {
1289
1331
  return 0;
1290
1332
  }
1291
1333
  }
1334
+ var LEFT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "{");
1335
+ var RIGHT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "}");
1336
+ var LEFT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "[");
1337
+ var RIGHT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "]");
1292
1338
  function eat_whitespaces(tokens, start) {
1293
1339
  let pos = start;
1294
1340
  while (pos < tokens.length && [4 /* SPACE */, 5 /* NEWLINE */].includes(tokens[pos].type)) {
@@ -1298,7 +1344,7 @@ function eat_whitespaces(tokens, start) {
1298
1344
  }
1299
1345
  function eat_parenthesis(tokens, start) {
1300
1346
  const firstToken = tokens[start];
1301
- if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}"].includes(firstToken.value)) {
1347
+ if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", "."].includes(firstToken.value)) {
1302
1348
  return firstToken;
1303
1349
  } else if (firstToken.type === 1 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle"].includes(firstToken.value.slice(1))) {
1304
1350
  return firstToken;
@@ -1337,9 +1383,13 @@ function find_closing_match(tokens, start, leftToken, rightToken) {
1337
1383
  }
1338
1384
  return pos - 1;
1339
1385
  }
1386
+ var LEFT_COMMAND = new TexToken(1 /* COMMAND */, "\\left");
1387
+ var RIGHT_COMMAND = new TexToken(1 /* COMMAND */, "\\right");
1340
1388
  function find_closing_right_command(tokens, start) {
1341
1389
  return find_closing_match(tokens, start, LEFT_COMMAND, RIGHT_COMMAND);
1342
1390
  }
1391
+ var BEGIN_COMMAND = new TexToken(1 /* COMMAND */, "\\begin");
1392
+ var END_COMMAND = new TexToken(1 /* COMMAND */, "\\end");
1343
1393
  function find_closing_end_command(tokens, start) {
1344
1394
  return find_closing_match(tokens, start, BEGIN_COMMAND, END_COMMAND);
1345
1395
  }
@@ -1373,7 +1423,8 @@ function tokenize(latex) {
1373
1423
  switch (firstChar) {
1374
1424
  case "%": {
1375
1425
  let newPos = pos + 1;
1376
- while (newPos < latex.length && latex[newPos] !== "\n") {
1426
+ while (newPos < latex.length && latex[newPos] !== `
1427
+ `) {
1377
1428
  newPos += 1;
1378
1429
  }
1379
1430
  token = new TexToken(3 /* COMMENT */, latex.slice(pos + 1, newPos));
@@ -1388,16 +1439,20 @@ function tokenize(latex) {
1388
1439
  token = new TexToken(6 /* CONTROL */, firstChar);
1389
1440
  pos++;
1390
1441
  break;
1391
- case "\n":
1442
+ case `
1443
+ `:
1392
1444
  token = new TexToken(5 /* NEWLINE */, firstChar);
1393
1445
  pos++;
1394
1446
  break;
1395
1447
  case "\r": {
1396
- if (pos + 1 < latex.length && latex[pos + 1] === "\n") {
1397
- token = new TexToken(5 /* NEWLINE */, "\n");
1448
+ if (pos + 1 < latex.length && latex[pos + 1] === `
1449
+ `) {
1450
+ token = new TexToken(5 /* NEWLINE */, `
1451
+ `);
1398
1452
  pos += 2;
1399
1453
  } else {
1400
- token = new TexToken(5 /* NEWLINE */, "\n");
1454
+ token = new TexToken(5 /* NEWLINE */, `
1455
+ `);
1401
1456
  pos++;
1402
1457
  }
1403
1458
  break;
@@ -1436,7 +1491,7 @@ function tokenize(latex) {
1436
1491
  token = new TexToken(0 /* ELEMENT */, latex.slice(pos, newPos));
1437
1492
  } else if (isalpha(firstChar)) {
1438
1493
  token = new TexToken(0 /* ELEMENT */, firstChar);
1439
- } else if ("+-*/=\'<>!.,;?()[]|".includes(firstChar)) {
1494
+ } else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
1440
1495
  token = new TexToken(0 /* ELEMENT */, firstChar);
1441
1496
  } else {
1442
1497
  token = new TexToken(7 /* UNKNOWN */, firstChar);
@@ -1464,87 +1519,6 @@ function tokenize(latex) {
1464
1519
  }
1465
1520
  return tokens;
1466
1521
  }
1467
- function passIgnoreWhitespaceBeforeScriptMark(tokens) {
1468
- const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
1469
- let out_tokens = [];
1470
- for (let i = 0;i < tokens.length; i++) {
1471
- if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
1472
- continue;
1473
- }
1474
- if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
1475
- continue;
1476
- }
1477
- out_tokens.push(tokens[i]);
1478
- }
1479
- return out_tokens;
1480
- }
1481
- function passExpandCustomTexMacros(tokens, customTexMacros) {
1482
- let out_tokens = [];
1483
- for (const token of tokens) {
1484
- if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
1485
- const expanded_tokens = tokenize(customTexMacros[token.value]);
1486
- out_tokens = out_tokens.concat(expanded_tokens);
1487
- } else {
1488
- out_tokens.push(token);
1489
- }
1490
- }
1491
- return out_tokens;
1492
- }
1493
- function parseTex(tex, customTexMacros) {
1494
- const parser = new LatexParser;
1495
- let tokens = tokenize(tex);
1496
- tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
1497
- tokens = passExpandCustomTexMacros(tokens, customTexMacros);
1498
- return parser.parse(tokens);
1499
- }
1500
- var UNARY_COMMANDS = [
1501
- "sqrt",
1502
- "text",
1503
- "bar",
1504
- "bold",
1505
- "boldsymbol",
1506
- "ddot",
1507
- "dot",
1508
- "hat",
1509
- "mathbb",
1510
- "mathbf",
1511
- "mathcal",
1512
- "mathfrak",
1513
- "mathit",
1514
- "mathrm",
1515
- "mathscr",
1516
- "mathsf",
1517
- "mathtt",
1518
- "operatorname",
1519
- "overbrace",
1520
- "overline",
1521
- "pmb",
1522
- "rm",
1523
- "tilde",
1524
- "underbrace",
1525
- "underline",
1526
- "vec",
1527
- "widehat",
1528
- "widetilde"
1529
- ];
1530
- var BINARY_COMMANDS = [
1531
- "frac",
1532
- "tfrac",
1533
- "binom",
1534
- "dbinom",
1535
- "dfrac",
1536
- "tbinom",
1537
- "overset"
1538
- ];
1539
- var EMPTY_NODE = new TexNode("empty", "");
1540
- var LEFT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "{");
1541
- var RIGHT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "}");
1542
- var LEFT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "[");
1543
- var RIGHT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "]");
1544
- var LEFT_COMMAND = new TexToken(1 /* COMMAND */, "\\left");
1545
- var RIGHT_COMMAND = new TexToken(1 /* COMMAND */, "\\right");
1546
- var BEGIN_COMMAND = new TexToken(1 /* COMMAND */, "\\begin");
1547
- var END_COMMAND = new TexToken(1 /* COMMAND */, "\\end");
1548
1522
 
1549
1523
  class LatexParserError extends Error {
1550
1524
  constructor(message) {
@@ -1575,7 +1549,8 @@ class LatexParser {
1575
1549
  if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
1576
1550
  continue;
1577
1551
  }
1578
- if (!this.newline_sensitive && res.content === "\n") {
1552
+ if (!this.newline_sensitive && res.content === `
1553
+ `) {
1579
1554
  continue;
1580
1555
  }
1581
1556
  }
@@ -1826,7 +1801,8 @@ class LatexParser {
1826
1801
  if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
1827
1802
  continue;
1828
1803
  }
1829
- if (!this.newline_sensitive && res.content === "\n") {
1804
+ if (!this.newline_sensitive && res.content === `
1805
+ `) {
1830
1806
  continue;
1831
1807
  }
1832
1808
  }
@@ -1845,10 +1821,52 @@ class LatexParser {
1845
1821
  return allRows;
1846
1822
  }
1847
1823
  }
1824
+ function passIgnoreWhitespaceBeforeScriptMark(tokens) {
1825
+ const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
1826
+ let out_tokens = [];
1827
+ for (let i = 0;i < tokens.length; i++) {
1828
+ if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
1829
+ continue;
1830
+ }
1831
+ if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
1832
+ continue;
1833
+ }
1834
+ out_tokens.push(tokens[i]);
1835
+ }
1836
+ return out_tokens;
1837
+ }
1838
+ function passExpandCustomTexMacros(tokens, customTexMacros) {
1839
+ let out_tokens = [];
1840
+ for (const token of tokens) {
1841
+ if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
1842
+ const expanded_tokens = tokenize(customTexMacros[token.value]);
1843
+ out_tokens = out_tokens.concat(expanded_tokens);
1844
+ } else {
1845
+ out_tokens.push(token);
1846
+ }
1847
+ }
1848
+ return out_tokens;
1849
+ }
1850
+ function parseTex(tex, customTexMacros) {
1851
+ const parser = new LatexParser;
1852
+ let tokens = tokenize(tex);
1853
+ tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
1854
+ tokens = passExpandCustomTexMacros(tokens, customTexMacros);
1855
+ return parser.parse(tokens);
1856
+ }
1848
1857
 
1849
1858
  // src/writer.ts
1859
+ var TYPST_INTRINSIC_SYMBOLS = [
1860
+ "dim",
1861
+ "id",
1862
+ "im",
1863
+ "mod",
1864
+ "Pr",
1865
+ "sech",
1866
+ "csch"
1867
+ ];
1850
1868
  function is_delimiter(c) {
1851
- return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
1869
+ return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "", "", "", ""].includes(c.content);
1852
1870
  }
1853
1871
  function convert_overset(node) {
1854
1872
  const [sup, base] = node.args;
@@ -1878,178 +1896,37 @@ function convert_overset(node) {
1878
1896
  sup: convertTree(sup)
1879
1897
  });
1880
1898
  }
1881
- function convertTree(node) {
1882
- switch (node.type) {
1883
- case "empty":
1884
- return new TypstNode("empty", "");
1885
- case "whitespace":
1886
- return new TypstNode("whitespace", node.content);
1887
- case "ordgroup":
1888
- return new TypstNode("group", "", node.args.map(convertTree));
1889
- case "element":
1890
- return new TypstNode("atom", convertToken(node.content));
1891
- case "symbol":
1892
- return new TypstNode("symbol", convertToken(node.content));
1893
- case "text":
1894
- return new TypstNode("text", node.content);
1895
- case "comment":
1896
- return new TypstNode("comment", node.content);
1897
- case "supsub": {
1898
- let { base, sup, sub } = node.data;
1899
- if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
1900
- return new TypstNode("funcCall", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
1901
- } else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
1902
- return new TypstNode("funcCall", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
1903
- }
1904
- const data = {
1905
- base: convertTree(base)
1906
- };
1907
- if (data.base.type === "empty") {
1908
- data.base = new TypstNode("text", "");
1909
- }
1910
- if (sup) {
1911
- data.sup = convertTree(sup);
1912
- }
1913
- if (sub) {
1914
- data.sub = convertTree(sub);
1915
- }
1916
- return new TypstNode("supsub", "", [], data);
1917
- }
1918
- case "leftright": {
1919
- const [left, body, right] = node.args;
1920
- const group = new TypstNode("group", "", node.args.map(convertTree));
1921
- if ([
1922
- "[]",
1923
- "()",
1924
- "\\{\\}",
1925
- "\\lfloor\\rfloor",
1926
- "\\lceil\\rceil",
1927
- "\\lfloor\\rceil"
1928
- ].includes(left.content + right.content)) {
1929
- return group;
1930
- }
1931
- return new TypstNode("funcCall", "lr", [group]);
1932
- }
1933
- case "binaryFunc": {
1934
- if (node.content === "\\overset") {
1935
- return convert_overset(node);
1936
- }
1937
- return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
1938
- }
1939
- case "unaryFunc": {
1940
- const arg0 = convertTree(node.args[0]);
1941
- if (node.content === "\\sqrt" && node.data) {
1942
- const data = convertTree(node.data);
1943
- return new TypstNode("funcCall", "root", [data, arg0]);
1944
- }
1945
- if (node.content === "\\mathbf") {
1946
- const inner = new TypstNode("funcCall", "bold", [arg0]);
1947
- return new TypstNode("funcCall", "upright", [inner]);
1948
- }
1949
- if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
1950
- return new TypstNode("symbol", arg0.content + arg0.content);
1951
- }
1952
- if (node.content === "\\operatorname") {
1953
- const body = node.args;
1954
- if (body.length !== 1 || body[0].type !== "text") {
1955
- throw new TypstWriterError(`Expecting body of \\operatorname to be text but got`, node);
1956
- }
1957
- const text = body[0].content;
1958
- if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
1959
- return new TypstNode("symbol", text);
1960
- } else {
1961
- return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
1962
- }
1963
- }
1964
- return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
1965
- }
1966
- case "beginend": {
1967
- const matrix = node.data;
1968
- const data = matrix.map((row) => row.map(convertTree));
1969
- if (node.content.startsWith("align")) {
1970
- return new TypstNode("align", "", [], data);
1971
- } else {
1972
- const res = new TypstNode("matrix", "", [], data);
1973
- res.setOptions({ delim: "#none" });
1974
- return res;
1975
- }
1976
- }
1977
- case "unknownMacro":
1978
- return new TypstNode("unknown", convertToken(node.content));
1979
- case "control":
1980
- if (node.content === "\\\\") {
1981
- return new TypstNode("symbol", "\\");
1982
- } else if (node.content === "\\,") {
1983
- return new TypstNode("symbol", "thin");
1984
- } else {
1985
- throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
1986
- }
1987
- default:
1988
- throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
1989
- }
1990
- }
1991
- function convertToken(token) {
1992
- if (/^[a-zA-Z0-9]$/.test(token)) {
1993
- return token;
1994
- } else if (token === "/") {
1995
- return "\\/";
1996
- } else if (token === "\\|") {
1997
- return "parallel";
1998
- } else if (token === "\\colon") {
1999
- return ":";
2000
- } else if (token === "\\\\") {
2001
- return "\\";
2002
- } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
2003
- return token;
2004
- } else if (token.startsWith("\\")) {
2005
- const symbol = token.slice(1);
2006
- if (symbolMap.has(symbol)) {
2007
- return symbolMap.get(symbol);
2008
- } else {
2009
- return symbol;
2010
- }
2011
- }
2012
- return token;
2013
- }
2014
- var TYPST_INTRINSIC_SYMBOLS = [
2015
- "dim",
2016
- "id",
2017
- "im",
2018
- "mod",
2019
- "Pr",
2020
- "sech",
2021
- "csch"
2022
- ];
2023
- var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
2024
- var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
2025
- var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
2026
- var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, "\n");
2027
-
2028
- class TypstWriterError extends Error {
2029
- node;
2030
- constructor(message, node) {
2031
- super(message);
2032
- this.name = "TypstWriterError";
2033
- this.node = node;
2034
- }
2035
- }
2036
-
2037
- class TypstWriter {
2038
- nonStrict;
2039
- preferTypstIntrinsic;
2040
- keepSpaces;
2041
- buffer = "";
2042
- queue = [];
2043
- insideFunctionDepth = 0;
2044
- constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
2045
- this.nonStrict = nonStrict;
2046
- this.preferTypstIntrinsic = preferTypstIntrinsic;
2047
- this.keepSpaces = keepSpaces;
2048
- }
2049
- writeBuffer(token) {
2050
- const str = token.toString();
2051
- if (str === "") {
2052
- return;
1899
+ var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
1900
+ var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
1901
+ var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
1902
+ var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, `
1903
+ `);
1904
+
1905
+ class TypstWriterError extends Error {
1906
+ node;
1907
+ constructor(message, node) {
1908
+ super(message);
1909
+ this.name = "TypstWriterError";
1910
+ this.node = node;
1911
+ }
1912
+ }
1913
+
1914
+ class TypstWriter {
1915
+ nonStrict;
1916
+ preferTypstIntrinsic;
1917
+ keepSpaces;
1918
+ buffer = "";
1919
+ queue = [];
1920
+ insideFunctionDepth = 0;
1921
+ constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
1922
+ this.nonStrict = nonStrict;
1923
+ this.preferTypstIntrinsic = preferTypstIntrinsic;
1924
+ this.keepSpaces = keepSpaces;
1925
+ }
1926
+ writeBuffer(token) {
1927
+ const str = token.toString();
1928
+ if (str === "") {
1929
+ return;
2053
1930
  }
2054
1931
  let no_need_space = false;
2055
1932
  no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
@@ -2058,7 +1935,8 @@ class TypstWriter {
2058
1935
  no_need_space ||= str === "'";
2059
1936
  no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
2060
1937
  no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
2061
- no_need_space ||= str.startsWith("\n");
1938
+ no_need_space ||= str.startsWith(`
1939
+ `);
2062
1940
  no_need_space ||= this.buffer === "";
2063
1941
  no_need_space ||= /^\s/.test(str);
2064
1942
  no_need_space ||= this.buffer.endsWith("&") && str === "=";
@@ -2095,7 +1973,8 @@ class TypstWriter {
2095
1973
  if (this.keepSpaces) {
2096
1974
  this.queue.push(new TypstToken(4 /* SPACE */, c));
2097
1975
  }
2098
- } else if (c === "\n") {
1976
+ } else if (c === `
1977
+ `) {
2099
1978
  this.queue.push(new TypstToken(0 /* SYMBOL */, c));
2100
1979
  } else {
2101
1980
  throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
@@ -2111,9 +1990,9 @@ class TypstWriter {
2111
1990
  let { base, sup, sub } = node.data;
2112
1991
  this.appendWithBracketsIfNeeded(base);
2113
1992
  let trailing_space_needed = false;
2114
- const has_prime = sup && sup.type === "atom" && sup.content === "\'";
1993
+ const has_prime = sup && sup.type === "atom" && sup.content === "'";
2115
1994
  if (has_prime) {
2116
- this.queue.push(new TypstToken(1 /* ELEMENT */, "\'"));
1995
+ this.queue.push(new TypstToken(1 /* ELEMENT */, "'"));
2117
1996
  trailing_space_needed = false;
2118
1997
  }
2119
1998
  if (sub) {
@@ -2261,6 +2140,146 @@ class TypstWriter {
2261
2140
  return this.buffer;
2262
2141
  }
2263
2142
  }
2143
+ function convertTree(node) {
2144
+ switch (node.type) {
2145
+ case "empty":
2146
+ return new TypstNode("empty", "");
2147
+ case "whitespace":
2148
+ return new TypstNode("whitespace", node.content);
2149
+ case "ordgroup":
2150
+ return new TypstNode("group", "", node.args.map(convertTree));
2151
+ case "element":
2152
+ return new TypstNode("atom", convertToken(node.content));
2153
+ case "symbol":
2154
+ return new TypstNode("symbol", convertToken(node.content));
2155
+ case "text":
2156
+ return new TypstNode("text", node.content);
2157
+ case "comment":
2158
+ return new TypstNode("comment", node.content);
2159
+ case "supsub": {
2160
+ let { base, sup, sub } = node.data;
2161
+ if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
2162
+ return new TypstNode("funcCall", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
2163
+ } else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
2164
+ return new TypstNode("funcCall", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
2165
+ }
2166
+ const data = {
2167
+ base: convertTree(base)
2168
+ };
2169
+ if (data.base.type === "empty") {
2170
+ data.base = new TypstNode("text", "");
2171
+ }
2172
+ if (sup) {
2173
+ data.sup = convertTree(sup);
2174
+ }
2175
+ if (sub) {
2176
+ data.sub = convertTree(sub);
2177
+ }
2178
+ return new TypstNode("supsub", "", [], data);
2179
+ }
2180
+ case "leftright": {
2181
+ const [left, body, right] = node.args;
2182
+ const group = new TypstNode("group", "", node.args.map(convertTree));
2183
+ if ([
2184
+ "[]",
2185
+ "()",
2186
+ "\\{\\}",
2187
+ "\\lfloor\\rfloor",
2188
+ "\\lceil\\rceil",
2189
+ "\\lfloor\\rceil"
2190
+ ].includes(left.content + right.content)) {
2191
+ return group;
2192
+ }
2193
+ if (right.content === ".") {
2194
+ group.args.pop();
2195
+ return group;
2196
+ } else if (left.content === ".") {
2197
+ group.args.shift();
2198
+ return new TypstNode("funcCall", "lr", [group]);
2199
+ }
2200
+ return new TypstNode("funcCall", "lr", [group]);
2201
+ }
2202
+ case "binaryFunc": {
2203
+ if (node.content === "\\overset") {
2204
+ return convert_overset(node);
2205
+ }
2206
+ return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
2207
+ }
2208
+ case "unaryFunc": {
2209
+ const arg0 = convertTree(node.args[0]);
2210
+ if (node.content === "\\sqrt" && node.data) {
2211
+ const data = convertTree(node.data);
2212
+ return new TypstNode("funcCall", "root", [data, arg0]);
2213
+ }
2214
+ if (node.content === "\\mathbf") {
2215
+ const inner = new TypstNode("funcCall", "bold", [arg0]);
2216
+ return new TypstNode("funcCall", "upright", [inner]);
2217
+ }
2218
+ if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
2219
+ return new TypstNode("symbol", arg0.content + arg0.content);
2220
+ }
2221
+ if (node.content === "\\operatorname") {
2222
+ const body = node.args;
2223
+ if (body.length !== 1 || body[0].type !== "text") {
2224
+ throw new TypstWriterError(`Expecting body of \\operatorname to be text but got`, node);
2225
+ }
2226
+ const text = body[0].content;
2227
+ if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
2228
+ return new TypstNode("symbol", text);
2229
+ } else {
2230
+ return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
2231
+ }
2232
+ }
2233
+ return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
2234
+ }
2235
+ case "beginend": {
2236
+ const matrix = node.data;
2237
+ const data = matrix.map((row) => row.map(convertTree));
2238
+ if (node.content.startsWith("align")) {
2239
+ return new TypstNode("align", "", [], data);
2240
+ } else {
2241
+ const res = new TypstNode("matrix", "", [], data);
2242
+ res.setOptions({ delim: "#none" });
2243
+ return res;
2244
+ }
2245
+ }
2246
+ case "unknownMacro":
2247
+ return new TypstNode("unknown", convertToken(node.content));
2248
+ case "control":
2249
+ if (node.content === "\\\\") {
2250
+ return new TypstNode("symbol", "\\");
2251
+ } else if (node.content === "\\,") {
2252
+ return new TypstNode("symbol", "thin");
2253
+ } else {
2254
+ throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
2255
+ }
2256
+ default:
2257
+ throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
2258
+ }
2259
+ }
2260
+ function convertToken(token) {
2261
+ if (/^[a-zA-Z0-9]$/.test(token)) {
2262
+ return token;
2263
+ } else if (token === "/") {
2264
+ return "\\/";
2265
+ } else if (token === "\\|") {
2266
+ return "parallel";
2267
+ } else if (token === "\\colon") {
2268
+ return ":";
2269
+ } else if (token === "\\\\") {
2270
+ return "\\";
2271
+ } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
2272
+ return token;
2273
+ } else if (token.startsWith("\\")) {
2274
+ const symbol = token.slice(1);
2275
+ if (symbolMap.has(symbol)) {
2276
+ return symbolMap.get(symbol);
2277
+ } else {
2278
+ return symbol;
2279
+ }
2280
+ }
2281
+ return token;
2282
+ }
2264
2283
 
2265
2284
  // src/typst-parser.ts
2266
2285
  function eat_primes2(tokens, start) {
@@ -2277,6 +2296,7 @@ function eat_identifier_name(typst, start) {
2277
2296
  }
2278
2297
  return typst.substring(start, pos);
2279
2298
  }
2299
+ var TYPST_EMPTY_NODE = new TypstNode("empty", "");
2280
2300
  function tokenize_typst(typst) {
2281
2301
  const tokens = [];
2282
2302
  let pos = 0;
@@ -2290,16 +2310,20 @@ function tokenize_typst(typst) {
2290
2310
  token = new TypstToken(6 /* CONTROL */, firstChar);
2291
2311
  pos++;
2292
2312
  break;
2293
- case "\n":
2313
+ case `
2314
+ `:
2294
2315
  token = new TypstToken(7 /* NEWLINE */, firstChar);
2295
2316
  pos++;
2296
2317
  break;
2297
2318
  case "\r": {
2298
- if (pos + 1 < typst.length && typst[pos + 1] === "\n") {
2299
- token = new TypstToken(7 /* NEWLINE */, "\n");
2319
+ if (pos + 1 < typst.length && typst[pos + 1] === `
2320
+ `) {
2321
+ token = new TypstToken(7 /* NEWLINE */, `
2322
+ `);
2300
2323
  pos += 2;
2301
2324
  } else {
2302
- token = new TypstToken(7 /* NEWLINE */, "\n");
2325
+ token = new TypstToken(7 /* NEWLINE */, `
2326
+ `);
2303
2327
  pos++;
2304
2328
  }
2305
2329
  break;
@@ -2316,7 +2340,8 @@ function tokenize_typst(typst) {
2316
2340
  case "/": {
2317
2341
  if (pos < typst.length && typst[pos + 1] === "/") {
2318
2342
  let newPos = pos + 2;
2319
- while (newPos < typst.length && typst[newPos] !== "\n") {
2343
+ while (newPos < typst.length && typst[newPos] !== `
2344
+ `) {
2320
2345
  newPos++;
2321
2346
  }
2322
2347
  token = new TypstToken(3 /* COMMENT */, typst.slice(pos + 2, newPos));
@@ -2368,7 +2393,7 @@ function tokenize_typst(typst) {
2368
2393
  newPos += 1;
2369
2394
  }
2370
2395
  token = new TypstToken(1 /* ELEMENT */, typst.slice(pos, newPos));
2371
- } else if ("+-*/=\'<>!.,;?()[]|".includes(firstChar)) {
2396
+ } else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
2372
2397
  token = new TypstToken(1 /* ELEMENT */, firstChar);
2373
2398
  } else if (isalpha(firstChar)) {
2374
2399
  const identifier = eat_identifier_name(typst, pos);
@@ -2427,6 +2452,7 @@ function primes(num) {
2427
2452
  }
2428
2453
  return res;
2429
2454
  }
2455
+ var DIV = new TypstNode("atom", "/");
2430
2456
  function next_non_whitespace(nodes, start) {
2431
2457
  let pos = start;
2432
2458
  while (pos < nodes.length && nodes[pos].type === "whitespace") {
@@ -2511,13 +2537,6 @@ function process_operators(nodes, parenthesis = false) {
2511
2537
  }
2512
2538
  }
2513
2539
  }
2514
- function parseTypst(typst) {
2515
- const parser = new TypstParser;
2516
- let tokens = tokenize_typst(typst);
2517
- return parser.parse(tokens);
2518
- }
2519
- var TYPST_EMPTY_NODE = new TypstNode("empty", "");
2520
- var DIV = new TypstNode("atom", "/");
2521
2540
 
2522
2541
  class TypstParserError extends Error {
2523
2542
  constructor(message) {
@@ -2558,7 +2577,8 @@ class TypstParser {
2558
2577
  if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
2559
2578
  continue;
2560
2579
  }
2561
- if (!this.newline_sensitive && res.content === "\n") {
2580
+ if (!this.newline_sensitive && res.content === `
2581
+ `) {
2562
2582
  continue;
2563
2583
  }
2564
2584
  }
@@ -2700,14 +2720,95 @@ class TypstParser {
2700
2720
  return args;
2701
2721
  }
2702
2722
  }
2723
+ function parseTypst(typst) {
2724
+ const parser = new TypstParser;
2725
+ let tokens = tokenize_typst(typst);
2726
+ return parser.parse(tokens);
2727
+ }
2703
2728
 
2704
2729
  // src/tex-writer.ts
2730
+ var TYPST_UNARY_FUNCTIONS = [
2731
+ "sqrt",
2732
+ "bold",
2733
+ "arrow",
2734
+ "upright",
2735
+ "lr",
2736
+ "op",
2737
+ "macron",
2738
+ "dot",
2739
+ "dot.double",
2740
+ "hat",
2741
+ "tilde",
2742
+ "overline",
2743
+ "underline",
2744
+ "bb",
2745
+ "cal",
2746
+ "frak"
2747
+ ];
2748
+ var TYPST_BINARY_FUNCTIONS = [
2749
+ "frac",
2750
+ "root",
2751
+ "overbrace",
2752
+ "underbrace"
2753
+ ];
2705
2754
  function apply_escape_if_needed2(c) {
2706
2755
  if (["{", "}", "%"].includes(c)) {
2707
2756
  return "\\" + c;
2708
2757
  }
2709
2758
  return c;
2710
2759
  }
2760
+
2761
+ class TexWriter {
2762
+ buffer = "";
2763
+ queue = [];
2764
+ writeBuffer(token) {
2765
+ const str = token.toString();
2766
+ let no_need_space = false;
2767
+ if (token.type === 4 /* SPACE */) {
2768
+ no_need_space = true;
2769
+ } else {
2770
+ no_need_space ||= /[{\(\[\|]$/.test(this.buffer);
2771
+ no_need_space ||= /\\\w+$/.test(this.buffer) && str === "[";
2772
+ no_need_space ||= /^[\.,;:!\?\(\)\]{}_^]$/.test(str);
2773
+ no_need_space ||= ["\\{", "\\}"].includes(str);
2774
+ no_need_space ||= str === "'";
2775
+ no_need_space ||= this.buffer.endsWith("_") || this.buffer.endsWith("^");
2776
+ no_need_space ||= /\s$/.test(this.buffer);
2777
+ no_need_space ||= /^\s/.test(str);
2778
+ no_need_space ||= this.buffer === "";
2779
+ no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
2780
+ no_need_space ||= this.buffer.endsWith("&") && str === "=";
2781
+ }
2782
+ if (!no_need_space) {
2783
+ this.buffer += " ";
2784
+ }
2785
+ this.buffer += str;
2786
+ }
2787
+ append(node) {
2788
+ const alignment_char = new TexNode("control", "&");
2789
+ const newline_char = new TexNode("control", "\\\\");
2790
+ if (node.type === "ordgroup" && array_includes(node.args, alignment_char)) {
2791
+ const rows = array_split(node.args, newline_char);
2792
+ const data = [];
2793
+ for (const row of rows) {
2794
+ const cells = array_split(row, alignment_char);
2795
+ data.push(cells.map((cell) => new TexNode("ordgroup", "", cell)));
2796
+ }
2797
+ node = new TexNode("beginend", "aligned", [], data);
2798
+ }
2799
+ this.queue = this.queue.concat(node.serialize());
2800
+ }
2801
+ flushQueue() {
2802
+ for (let i = 0;i < this.queue.length; i++) {
2803
+ this.writeBuffer(this.queue[i]);
2804
+ }
2805
+ this.queue = [];
2806
+ }
2807
+ finalize() {
2808
+ this.flushQueue();
2809
+ return this.buffer;
2810
+ }
2811
+ }
2711
2812
  function convert_typst_node_to_tex(node) {
2712
2813
  if (node.eq(new TypstNode("symbol", "eq.def"))) {
2713
2814
  return new TexNode("binaryFunc", "\\overset", [
@@ -2842,82 +2943,6 @@ function typst_token_to_tex(token) {
2842
2943
  }
2843
2944
  return "\\" + token;
2844
2945
  }
2845
- var TYPST_UNARY_FUNCTIONS = [
2846
- "sqrt",
2847
- "bold",
2848
- "arrow",
2849
- "upright",
2850
- "lr",
2851
- "op",
2852
- "macron",
2853
- "dot",
2854
- "dot.double",
2855
- "hat",
2856
- "tilde",
2857
- "overline",
2858
- "underline",
2859
- "bb",
2860
- "cal",
2861
- "frak"
2862
- ];
2863
- var TYPST_BINARY_FUNCTIONS = [
2864
- "frac",
2865
- "root",
2866
- "overbrace",
2867
- "underbrace"
2868
- ];
2869
-
2870
- class TexWriter {
2871
- buffer = "";
2872
- queue = [];
2873
- writeBuffer(token) {
2874
- const str = token.toString();
2875
- let no_need_space = false;
2876
- if (token.type === 4 /* SPACE */) {
2877
- no_need_space = true;
2878
- } else {
2879
- no_need_space ||= /[{\(\[\|]$/.test(this.buffer);
2880
- no_need_space ||= /\\\w+$/.test(this.buffer) && str === "[";
2881
- no_need_space ||= /^[\.,;:!\?\(\)\]{}_^]$/.test(str);
2882
- no_need_space ||= ["\\{", "\\}"].includes(str);
2883
- no_need_space ||= str === "'";
2884
- no_need_space ||= this.buffer.endsWith("_") || this.buffer.endsWith("^");
2885
- no_need_space ||= /\s$/.test(this.buffer);
2886
- no_need_space ||= /^\s/.test(str);
2887
- no_need_space ||= this.buffer === "";
2888
- no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
2889
- no_need_space ||= this.buffer.endsWith("&") && str === "=";
2890
- }
2891
- if (!no_need_space) {
2892
- this.buffer += " ";
2893
- }
2894
- this.buffer += str;
2895
- }
2896
- append(node) {
2897
- const alignment_char = new TexNode("control", "&");
2898
- const newline_char = new TexNode("control", "\\\\");
2899
- if (node.type === "ordgroup" && array_includes(node.args, alignment_char)) {
2900
- const rows = array_split(node.args, newline_char);
2901
- const data = [];
2902
- for (const row of rows) {
2903
- const cells = array_split(row, alignment_char);
2904
- data.push(cells.map((cell) => new TexNode("ordgroup", "", cell)));
2905
- }
2906
- node = new TexNode("beginend", "aligned", [], data);
2907
- }
2908
- this.queue = this.queue.concat(node.serialize());
2909
- }
2910
- flushQueue() {
2911
- for (let i = 0;i < this.queue.length; i++) {
2912
- this.writeBuffer(this.queue[i]);
2913
- }
2914
- this.queue = [];
2915
- }
2916
- finalize() {
2917
- this.flushQueue();
2918
- return this.buffer;
2919
- }
2920
- }
2921
2946
 
2922
2947
  // src/index.ts
2923
2948
  function tex2typst(tex, options) {
@@ -2940,16 +2965,16 @@ function tex2typst(tex, options) {
2940
2965
  }
2941
2966
  const texTree = parseTex(tex, opt.customTexMacros);
2942
2967
  const typstTree = convertTree(texTree);
2943
- const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
2944
- writer2.serialize(typstTree);
2945
- return writer2.finalize();
2968
+ const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
2969
+ writer.serialize(typstTree);
2970
+ return writer.finalize();
2946
2971
  }
2947
2972
  function typst2tex(typst) {
2948
2973
  const typstTree = parseTypst(typst);
2949
2974
  const texTree = convert_typst_node_to_tex(typstTree);
2950
- const writer2 = new TexWriter;
2951
- writer2.append(texTree);
2952
- return writer2.finalize();
2975
+ const writer = new TexWriter;
2976
+ writer.append(texTree);
2977
+ return writer.finalize();
2953
2978
  }
2954
2979
  export {
2955
2980
  typst2tex,