tex2typst 0.3.0-alpha → 0.3.0-beta-2
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 +406 -372
- package/dist/tex2typst.min.js +19 -1
- package/package.json +1 -1
- package/src/tex-parser.ts +13 -2
- package/src/writer.ts +9 -0
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", "
|
|
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 */,
|
|
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 */,
|
|
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] !==
|
|
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
|
|
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] ===
|
|
1397
|
-
|
|
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 */,
|
|
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 ("
|
|
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 ===
|
|
1552
|
+
if (!this.newline_sensitive && res.content === `
|
|
1553
|
+
`) {
|
|
1579
1554
|
continue;
|
|
1580
1555
|
}
|
|
1581
1556
|
}
|
|
@@ -1679,6 +1654,9 @@ class LatexParser {
|
|
|
1679
1654
|
switch (controlChar) {
|
|
1680
1655
|
case "{":
|
|
1681
1656
|
const posClosingBracket = find_closing_match(tokens, start, LEFT_CURLY_BRACKET, RIGHT_CURLY_BRACKET);
|
|
1657
|
+
if (posClosingBracket === -1) {
|
|
1658
|
+
throw new LatexParserError("Unmatched '{'");
|
|
1659
|
+
}
|
|
1682
1660
|
const exprInside = tokens.slice(start + 1, posClosingBracket);
|
|
1683
1661
|
return [this.parse(exprInside), posClosingBracket + 1];
|
|
1684
1662
|
case "}":
|
|
@@ -1714,9 +1692,15 @@ class LatexParser {
|
|
|
1714
1692
|
}
|
|
1715
1693
|
return [new TexNode("symbol", command), pos];
|
|
1716
1694
|
case 1: {
|
|
1695
|
+
if (pos >= tokens.length) {
|
|
1696
|
+
throw new LatexParserError("Expecting argument for " + command);
|
|
1697
|
+
}
|
|
1717
1698
|
if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
|
|
1718
1699
|
const posLeftSquareBracket = pos;
|
|
1719
1700
|
const posRightSquareBracket = find_closing_match(tokens, pos, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET);
|
|
1701
|
+
if (posRightSquareBracket === -1) {
|
|
1702
|
+
throw new LatexParserError("No matching right square bracket for [");
|
|
1703
|
+
}
|
|
1720
1704
|
const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
|
|
1721
1705
|
const exponent = this.parse(exprInside);
|
|
1722
1706
|
const [arg12, newPos2] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
|
|
@@ -1826,7 +1810,8 @@ class LatexParser {
|
|
|
1826
1810
|
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1827
1811
|
continue;
|
|
1828
1812
|
}
|
|
1829
|
-
if (!this.newline_sensitive && res.content ===
|
|
1813
|
+
if (!this.newline_sensitive && res.content === `
|
|
1814
|
+
`) {
|
|
1830
1815
|
continue;
|
|
1831
1816
|
}
|
|
1832
1817
|
}
|
|
@@ -1845,10 +1830,52 @@ class LatexParser {
|
|
|
1845
1830
|
return allRows;
|
|
1846
1831
|
}
|
|
1847
1832
|
}
|
|
1833
|
+
function passIgnoreWhitespaceBeforeScriptMark(tokens) {
|
|
1834
|
+
const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
|
|
1835
|
+
let out_tokens = [];
|
|
1836
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
1837
|
+
if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
|
|
1838
|
+
continue;
|
|
1839
|
+
}
|
|
1840
|
+
if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
out_tokens.push(tokens[i]);
|
|
1844
|
+
}
|
|
1845
|
+
return out_tokens;
|
|
1846
|
+
}
|
|
1847
|
+
function passExpandCustomTexMacros(tokens, customTexMacros) {
|
|
1848
|
+
let out_tokens = [];
|
|
1849
|
+
for (const token of tokens) {
|
|
1850
|
+
if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
|
|
1851
|
+
const expanded_tokens = tokenize(customTexMacros[token.value]);
|
|
1852
|
+
out_tokens = out_tokens.concat(expanded_tokens);
|
|
1853
|
+
} else {
|
|
1854
|
+
out_tokens.push(token);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
return out_tokens;
|
|
1858
|
+
}
|
|
1859
|
+
function parseTex(tex, customTexMacros) {
|
|
1860
|
+
const parser = new LatexParser;
|
|
1861
|
+
let tokens = tokenize(tex);
|
|
1862
|
+
tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
|
|
1863
|
+
tokens = passExpandCustomTexMacros(tokens, customTexMacros);
|
|
1864
|
+
return parser.parse(tokens);
|
|
1865
|
+
}
|
|
1848
1866
|
|
|
1849
1867
|
// src/writer.ts
|
|
1868
|
+
var TYPST_INTRINSIC_SYMBOLS = [
|
|
1869
|
+
"dim",
|
|
1870
|
+
"id",
|
|
1871
|
+
"im",
|
|
1872
|
+
"mod",
|
|
1873
|
+
"Pr",
|
|
1874
|
+
"sech",
|
|
1875
|
+
"csch"
|
|
1876
|
+
];
|
|
1850
1877
|
function is_delimiter(c) {
|
|
1851
|
-
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "
|
|
1878
|
+
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "⌊", "⌋", "⌈", "⌉"].includes(c.content);
|
|
1852
1879
|
}
|
|
1853
1880
|
function convert_overset(node) {
|
|
1854
1881
|
const [sup, base] = node.args;
|
|
@@ -1878,178 +1905,37 @@ function convert_overset(node) {
|
|
|
1878
1905
|
sup: convertTree(sup)
|
|
1879
1906
|
});
|
|
1880
1907
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
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;
|
|
1908
|
+
var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
|
|
1909
|
+
var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
|
|
1910
|
+
var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
1911
|
+
var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, `
|
|
1912
|
+
`);
|
|
1913
|
+
|
|
1914
|
+
class TypstWriterError extends Error {
|
|
1915
|
+
node;
|
|
1916
|
+
constructor(message, node) {
|
|
1917
|
+
super(message);
|
|
1918
|
+
this.name = "TypstWriterError";
|
|
1919
|
+
this.node = node;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
class TypstWriter {
|
|
1924
|
+
nonStrict;
|
|
1925
|
+
preferTypstIntrinsic;
|
|
1926
|
+
keepSpaces;
|
|
1927
|
+
buffer = "";
|
|
1928
|
+
queue = [];
|
|
1929
|
+
insideFunctionDepth = 0;
|
|
1930
|
+
constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
|
|
1931
|
+
this.nonStrict = nonStrict;
|
|
1932
|
+
this.preferTypstIntrinsic = preferTypstIntrinsic;
|
|
1933
|
+
this.keepSpaces = keepSpaces;
|
|
1934
|
+
}
|
|
1935
|
+
writeBuffer(token) {
|
|
1936
|
+
const str = token.toString();
|
|
1937
|
+
if (str === "") {
|
|
1938
|
+
return;
|
|
2053
1939
|
}
|
|
2054
1940
|
let no_need_space = false;
|
|
2055
1941
|
no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
@@ -2058,7 +1944,8 @@ class TypstWriter {
|
|
|
2058
1944
|
no_need_space ||= str === "'";
|
|
2059
1945
|
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
2060
1946
|
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
2061
|
-
no_need_space ||= str.startsWith(
|
|
1947
|
+
no_need_space ||= str.startsWith(`
|
|
1948
|
+
`);
|
|
2062
1949
|
no_need_space ||= this.buffer === "";
|
|
2063
1950
|
no_need_space ||= /^\s/.test(str);
|
|
2064
1951
|
no_need_space ||= this.buffer.endsWith("&") && str === "=";
|
|
@@ -2095,7 +1982,8 @@ class TypstWriter {
|
|
|
2095
1982
|
if (this.keepSpaces) {
|
|
2096
1983
|
this.queue.push(new TypstToken(4 /* SPACE */, c));
|
|
2097
1984
|
}
|
|
2098
|
-
} else if (c ===
|
|
1985
|
+
} else if (c === `
|
|
1986
|
+
`) {
|
|
2099
1987
|
this.queue.push(new TypstToken(0 /* SYMBOL */, c));
|
|
2100
1988
|
} else {
|
|
2101
1989
|
throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
|
|
@@ -2111,9 +1999,9 @@ class TypstWriter {
|
|
|
2111
1999
|
let { base, sup, sub } = node.data;
|
|
2112
2000
|
this.appendWithBracketsIfNeeded(base);
|
|
2113
2001
|
let trailing_space_needed = false;
|
|
2114
|
-
const has_prime = sup && sup.type === "atom" && sup.content === "
|
|
2002
|
+
const has_prime = sup && sup.type === "atom" && sup.content === "'";
|
|
2115
2003
|
if (has_prime) {
|
|
2116
|
-
this.queue.push(new TypstToken(1 /* ELEMENT */, "
|
|
2004
|
+
this.queue.push(new TypstToken(1 /* ELEMENT */, "'"));
|
|
2117
2005
|
trailing_space_needed = false;
|
|
2118
2006
|
}
|
|
2119
2007
|
if (sub) {
|
|
@@ -2261,6 +2149,146 @@ class TypstWriter {
|
|
|
2261
2149
|
return this.buffer;
|
|
2262
2150
|
}
|
|
2263
2151
|
}
|
|
2152
|
+
function convertTree(node) {
|
|
2153
|
+
switch (node.type) {
|
|
2154
|
+
case "empty":
|
|
2155
|
+
return new TypstNode("empty", "");
|
|
2156
|
+
case "whitespace":
|
|
2157
|
+
return new TypstNode("whitespace", node.content);
|
|
2158
|
+
case "ordgroup":
|
|
2159
|
+
return new TypstNode("group", "", node.args.map(convertTree));
|
|
2160
|
+
case "element":
|
|
2161
|
+
return new TypstNode("atom", convertToken(node.content));
|
|
2162
|
+
case "symbol":
|
|
2163
|
+
return new TypstNode("symbol", convertToken(node.content));
|
|
2164
|
+
case "text":
|
|
2165
|
+
return new TypstNode("text", node.content);
|
|
2166
|
+
case "comment":
|
|
2167
|
+
return new TypstNode("comment", node.content);
|
|
2168
|
+
case "supsub": {
|
|
2169
|
+
let { base, sup, sub } = node.data;
|
|
2170
|
+
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
2171
|
+
return new TypstNode("funcCall", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
|
|
2172
|
+
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
2173
|
+
return new TypstNode("funcCall", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
|
|
2174
|
+
}
|
|
2175
|
+
const data = {
|
|
2176
|
+
base: convertTree(base)
|
|
2177
|
+
};
|
|
2178
|
+
if (data.base.type === "empty") {
|
|
2179
|
+
data.base = new TypstNode("text", "");
|
|
2180
|
+
}
|
|
2181
|
+
if (sup) {
|
|
2182
|
+
data.sup = convertTree(sup);
|
|
2183
|
+
}
|
|
2184
|
+
if (sub) {
|
|
2185
|
+
data.sub = convertTree(sub);
|
|
2186
|
+
}
|
|
2187
|
+
return new TypstNode("supsub", "", [], data);
|
|
2188
|
+
}
|
|
2189
|
+
case "leftright": {
|
|
2190
|
+
const [left, body, right] = node.args;
|
|
2191
|
+
const group = new TypstNode("group", "", node.args.map(convertTree));
|
|
2192
|
+
if ([
|
|
2193
|
+
"[]",
|
|
2194
|
+
"()",
|
|
2195
|
+
"\\{\\}",
|
|
2196
|
+
"\\lfloor\\rfloor",
|
|
2197
|
+
"\\lceil\\rceil",
|
|
2198
|
+
"\\lfloor\\rceil"
|
|
2199
|
+
].includes(left.content + right.content)) {
|
|
2200
|
+
return group;
|
|
2201
|
+
}
|
|
2202
|
+
if (right.content === ".") {
|
|
2203
|
+
group.args.pop();
|
|
2204
|
+
return group;
|
|
2205
|
+
} else if (left.content === ".") {
|
|
2206
|
+
group.args.shift();
|
|
2207
|
+
return new TypstNode("funcCall", "lr", [group]);
|
|
2208
|
+
}
|
|
2209
|
+
return new TypstNode("funcCall", "lr", [group]);
|
|
2210
|
+
}
|
|
2211
|
+
case "binaryFunc": {
|
|
2212
|
+
if (node.content === "\\overset") {
|
|
2213
|
+
return convert_overset(node);
|
|
2214
|
+
}
|
|
2215
|
+
return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
|
|
2216
|
+
}
|
|
2217
|
+
case "unaryFunc": {
|
|
2218
|
+
const arg0 = convertTree(node.args[0]);
|
|
2219
|
+
if (node.content === "\\sqrt" && node.data) {
|
|
2220
|
+
const data = convertTree(node.data);
|
|
2221
|
+
return new TypstNode("funcCall", "root", [data, arg0]);
|
|
2222
|
+
}
|
|
2223
|
+
if (node.content === "\\mathbf") {
|
|
2224
|
+
const inner = new TypstNode("funcCall", "bold", [arg0]);
|
|
2225
|
+
return new TypstNode("funcCall", "upright", [inner]);
|
|
2226
|
+
}
|
|
2227
|
+
if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
|
|
2228
|
+
return new TypstNode("symbol", arg0.content + arg0.content);
|
|
2229
|
+
}
|
|
2230
|
+
if (node.content === "\\operatorname") {
|
|
2231
|
+
const body = node.args;
|
|
2232
|
+
if (body.length !== 1 || body[0].type !== "text") {
|
|
2233
|
+
throw new TypstWriterError(`Expecting body of \\operatorname to be text but got`, node);
|
|
2234
|
+
}
|
|
2235
|
+
const text = body[0].content;
|
|
2236
|
+
if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
|
|
2237
|
+
return new TypstNode("symbol", text);
|
|
2238
|
+
} else {
|
|
2239
|
+
return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
|
|
2243
|
+
}
|
|
2244
|
+
case "beginend": {
|
|
2245
|
+
const matrix = node.data;
|
|
2246
|
+
const data = matrix.map((row) => row.map(convertTree));
|
|
2247
|
+
if (node.content.startsWith("align")) {
|
|
2248
|
+
return new TypstNode("align", "", [], data);
|
|
2249
|
+
} else {
|
|
2250
|
+
const res = new TypstNode("matrix", "", [], data);
|
|
2251
|
+
res.setOptions({ delim: "#none" });
|
|
2252
|
+
return res;
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
case "unknownMacro":
|
|
2256
|
+
return new TypstNode("unknown", convertToken(node.content));
|
|
2257
|
+
case "control":
|
|
2258
|
+
if (node.content === "\\\\") {
|
|
2259
|
+
return new TypstNode("symbol", "\\");
|
|
2260
|
+
} else if (node.content === "\\,") {
|
|
2261
|
+
return new TypstNode("symbol", "thin");
|
|
2262
|
+
} else {
|
|
2263
|
+
throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
|
|
2264
|
+
}
|
|
2265
|
+
default:
|
|
2266
|
+
throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
function convertToken(token) {
|
|
2270
|
+
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
2271
|
+
return token;
|
|
2272
|
+
} else if (token === "/") {
|
|
2273
|
+
return "\\/";
|
|
2274
|
+
} else if (token === "\\|") {
|
|
2275
|
+
return "parallel";
|
|
2276
|
+
} else if (token === "\\colon") {
|
|
2277
|
+
return ":";
|
|
2278
|
+
} else if (token === "\\\\") {
|
|
2279
|
+
return "\\";
|
|
2280
|
+
} else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
|
|
2281
|
+
return token;
|
|
2282
|
+
} else if (token.startsWith("\\")) {
|
|
2283
|
+
const symbol = token.slice(1);
|
|
2284
|
+
if (symbolMap.has(symbol)) {
|
|
2285
|
+
return symbolMap.get(symbol);
|
|
2286
|
+
} else {
|
|
2287
|
+
return symbol;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
return token;
|
|
2291
|
+
}
|
|
2264
2292
|
|
|
2265
2293
|
// src/typst-parser.ts
|
|
2266
2294
|
function eat_primes2(tokens, start) {
|
|
@@ -2277,6 +2305,7 @@ function eat_identifier_name(typst, start) {
|
|
|
2277
2305
|
}
|
|
2278
2306
|
return typst.substring(start, pos);
|
|
2279
2307
|
}
|
|
2308
|
+
var TYPST_EMPTY_NODE = new TypstNode("empty", "");
|
|
2280
2309
|
function tokenize_typst(typst) {
|
|
2281
2310
|
const tokens = [];
|
|
2282
2311
|
let pos = 0;
|
|
@@ -2290,16 +2319,20 @@ function tokenize_typst(typst) {
|
|
|
2290
2319
|
token = new TypstToken(6 /* CONTROL */, firstChar);
|
|
2291
2320
|
pos++;
|
|
2292
2321
|
break;
|
|
2293
|
-
case
|
|
2322
|
+
case `
|
|
2323
|
+
`:
|
|
2294
2324
|
token = new TypstToken(7 /* NEWLINE */, firstChar);
|
|
2295
2325
|
pos++;
|
|
2296
2326
|
break;
|
|
2297
2327
|
case "\r": {
|
|
2298
|
-
if (pos + 1 < typst.length && typst[pos + 1] ===
|
|
2299
|
-
|
|
2328
|
+
if (pos + 1 < typst.length && typst[pos + 1] === `
|
|
2329
|
+
`) {
|
|
2330
|
+
token = new TypstToken(7 /* NEWLINE */, `
|
|
2331
|
+
`);
|
|
2300
2332
|
pos += 2;
|
|
2301
2333
|
} else {
|
|
2302
|
-
token = new TypstToken(7 /* NEWLINE */,
|
|
2334
|
+
token = new TypstToken(7 /* NEWLINE */, `
|
|
2335
|
+
`);
|
|
2303
2336
|
pos++;
|
|
2304
2337
|
}
|
|
2305
2338
|
break;
|
|
@@ -2316,7 +2349,8 @@ function tokenize_typst(typst) {
|
|
|
2316
2349
|
case "/": {
|
|
2317
2350
|
if (pos < typst.length && typst[pos + 1] === "/") {
|
|
2318
2351
|
let newPos = pos + 2;
|
|
2319
|
-
while (newPos < typst.length && typst[newPos] !==
|
|
2352
|
+
while (newPos < typst.length && typst[newPos] !== `
|
|
2353
|
+
`) {
|
|
2320
2354
|
newPos++;
|
|
2321
2355
|
}
|
|
2322
2356
|
token = new TypstToken(3 /* COMMENT */, typst.slice(pos + 2, newPos));
|
|
@@ -2368,7 +2402,7 @@ function tokenize_typst(typst) {
|
|
|
2368
2402
|
newPos += 1;
|
|
2369
2403
|
}
|
|
2370
2404
|
token = new TypstToken(1 /* ELEMENT */, typst.slice(pos, newPos));
|
|
2371
|
-
} else if ("
|
|
2405
|
+
} else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
|
|
2372
2406
|
token = new TypstToken(1 /* ELEMENT */, firstChar);
|
|
2373
2407
|
} else if (isalpha(firstChar)) {
|
|
2374
2408
|
const identifier = eat_identifier_name(typst, pos);
|
|
@@ -2427,6 +2461,7 @@ function primes(num) {
|
|
|
2427
2461
|
}
|
|
2428
2462
|
return res;
|
|
2429
2463
|
}
|
|
2464
|
+
var DIV = new TypstNode("atom", "/");
|
|
2430
2465
|
function next_non_whitespace(nodes, start) {
|
|
2431
2466
|
let pos = start;
|
|
2432
2467
|
while (pos < nodes.length && nodes[pos].type === "whitespace") {
|
|
@@ -2511,13 +2546,6 @@ function process_operators(nodes, parenthesis = false) {
|
|
|
2511
2546
|
}
|
|
2512
2547
|
}
|
|
2513
2548
|
}
|
|
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
2549
|
|
|
2522
2550
|
class TypstParserError extends Error {
|
|
2523
2551
|
constructor(message) {
|
|
@@ -2558,7 +2586,8 @@ class TypstParser {
|
|
|
2558
2586
|
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
2559
2587
|
continue;
|
|
2560
2588
|
}
|
|
2561
|
-
if (!this.newline_sensitive && res.content ===
|
|
2589
|
+
if (!this.newline_sensitive && res.content === `
|
|
2590
|
+
`) {
|
|
2562
2591
|
continue;
|
|
2563
2592
|
}
|
|
2564
2593
|
}
|
|
@@ -2700,14 +2729,95 @@ class TypstParser {
|
|
|
2700
2729
|
return args;
|
|
2701
2730
|
}
|
|
2702
2731
|
}
|
|
2732
|
+
function parseTypst(typst) {
|
|
2733
|
+
const parser = new TypstParser;
|
|
2734
|
+
let tokens = tokenize_typst(typst);
|
|
2735
|
+
return parser.parse(tokens);
|
|
2736
|
+
}
|
|
2703
2737
|
|
|
2704
2738
|
// src/tex-writer.ts
|
|
2739
|
+
var TYPST_UNARY_FUNCTIONS = [
|
|
2740
|
+
"sqrt",
|
|
2741
|
+
"bold",
|
|
2742
|
+
"arrow",
|
|
2743
|
+
"upright",
|
|
2744
|
+
"lr",
|
|
2745
|
+
"op",
|
|
2746
|
+
"macron",
|
|
2747
|
+
"dot",
|
|
2748
|
+
"dot.double",
|
|
2749
|
+
"hat",
|
|
2750
|
+
"tilde",
|
|
2751
|
+
"overline",
|
|
2752
|
+
"underline",
|
|
2753
|
+
"bb",
|
|
2754
|
+
"cal",
|
|
2755
|
+
"frak"
|
|
2756
|
+
];
|
|
2757
|
+
var TYPST_BINARY_FUNCTIONS = [
|
|
2758
|
+
"frac",
|
|
2759
|
+
"root",
|
|
2760
|
+
"overbrace",
|
|
2761
|
+
"underbrace"
|
|
2762
|
+
];
|
|
2705
2763
|
function apply_escape_if_needed2(c) {
|
|
2706
2764
|
if (["{", "}", "%"].includes(c)) {
|
|
2707
2765
|
return "\\" + c;
|
|
2708
2766
|
}
|
|
2709
2767
|
return c;
|
|
2710
2768
|
}
|
|
2769
|
+
|
|
2770
|
+
class TexWriter {
|
|
2771
|
+
buffer = "";
|
|
2772
|
+
queue = [];
|
|
2773
|
+
writeBuffer(token) {
|
|
2774
|
+
const str = token.toString();
|
|
2775
|
+
let no_need_space = false;
|
|
2776
|
+
if (token.type === 4 /* SPACE */) {
|
|
2777
|
+
no_need_space = true;
|
|
2778
|
+
} else {
|
|
2779
|
+
no_need_space ||= /[{\(\[\|]$/.test(this.buffer);
|
|
2780
|
+
no_need_space ||= /\\\w+$/.test(this.buffer) && str === "[";
|
|
2781
|
+
no_need_space ||= /^[\.,;:!\?\(\)\]{}_^]$/.test(str);
|
|
2782
|
+
no_need_space ||= ["\\{", "\\}"].includes(str);
|
|
2783
|
+
no_need_space ||= str === "'";
|
|
2784
|
+
no_need_space ||= this.buffer.endsWith("_") || this.buffer.endsWith("^");
|
|
2785
|
+
no_need_space ||= /\s$/.test(this.buffer);
|
|
2786
|
+
no_need_space ||= /^\s/.test(str);
|
|
2787
|
+
no_need_space ||= this.buffer === "";
|
|
2788
|
+
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
2789
|
+
no_need_space ||= this.buffer.endsWith("&") && str === "=";
|
|
2790
|
+
}
|
|
2791
|
+
if (!no_need_space) {
|
|
2792
|
+
this.buffer += " ";
|
|
2793
|
+
}
|
|
2794
|
+
this.buffer += str;
|
|
2795
|
+
}
|
|
2796
|
+
append(node) {
|
|
2797
|
+
const alignment_char = new TexNode("control", "&");
|
|
2798
|
+
const newline_char = new TexNode("control", "\\\\");
|
|
2799
|
+
if (node.type === "ordgroup" && array_includes(node.args, alignment_char)) {
|
|
2800
|
+
const rows = array_split(node.args, newline_char);
|
|
2801
|
+
const data = [];
|
|
2802
|
+
for (const row of rows) {
|
|
2803
|
+
const cells = array_split(row, alignment_char);
|
|
2804
|
+
data.push(cells.map((cell) => new TexNode("ordgroup", "", cell)));
|
|
2805
|
+
}
|
|
2806
|
+
node = new TexNode("beginend", "aligned", [], data);
|
|
2807
|
+
}
|
|
2808
|
+
this.queue = this.queue.concat(node.serialize());
|
|
2809
|
+
}
|
|
2810
|
+
flushQueue() {
|
|
2811
|
+
for (let i = 0;i < this.queue.length; i++) {
|
|
2812
|
+
this.writeBuffer(this.queue[i]);
|
|
2813
|
+
}
|
|
2814
|
+
this.queue = [];
|
|
2815
|
+
}
|
|
2816
|
+
finalize() {
|
|
2817
|
+
this.flushQueue();
|
|
2818
|
+
return this.buffer;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2711
2821
|
function convert_typst_node_to_tex(node) {
|
|
2712
2822
|
if (node.eq(new TypstNode("symbol", "eq.def"))) {
|
|
2713
2823
|
return new TexNode("binaryFunc", "\\overset", [
|
|
@@ -2842,82 +2952,6 @@ function typst_token_to_tex(token) {
|
|
|
2842
2952
|
}
|
|
2843
2953
|
return "\\" + token;
|
|
2844
2954
|
}
|
|
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
2955
|
|
|
2922
2956
|
// src/index.ts
|
|
2923
2957
|
function tex2typst(tex, options) {
|
|
@@ -2940,16 +2974,16 @@ function tex2typst(tex, options) {
|
|
|
2940
2974
|
}
|
|
2941
2975
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
2942
2976
|
const typstTree = convertTree(texTree);
|
|
2943
|
-
const
|
|
2944
|
-
|
|
2945
|
-
return
|
|
2977
|
+
const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
|
|
2978
|
+
writer.serialize(typstTree);
|
|
2979
|
+
return writer.finalize();
|
|
2946
2980
|
}
|
|
2947
2981
|
function typst2tex(typst) {
|
|
2948
2982
|
const typstTree = parseTypst(typst);
|
|
2949
2983
|
const texTree = convert_typst_node_to_tex(typstTree);
|
|
2950
|
-
const
|
|
2951
|
-
|
|
2952
|
-
return
|
|
2984
|
+
const writer = new TexWriter;
|
|
2985
|
+
writer.append(texTree);
|
|
2986
|
+
return writer.finalize();
|
|
2953
2987
|
}
|
|
2954
2988
|
export {
|
|
2955
2989
|
typst2tex,
|