tex2typst 0.2.12 → 0.2.15
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 +307 -245
- package/dist/tex-parser.d.ts +28 -0
- package/dist/tex2typst.min.js +1 -1
- package/dist/types.d.ts +47 -6
- package/dist/writer.d.ts +7 -7
- package/package.json +2 -2
- package/src/index.ts +4 -3
- package/src/{parser.ts → tex-parser.ts} +119 -111
- package/src/types.ts +99 -10
- package/src/writer.ts +255 -197
- package/dist/parser.d.ts +0 -28
package/dist/index.js
CHANGED
|
@@ -1034,7 +1034,73 @@ for (const [key, value] of map_from_official_docs) {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
1036
1036
|
|
|
1037
|
-
// src/
|
|
1037
|
+
// src/types.ts
|
|
1038
|
+
class TexNode {
|
|
1039
|
+
type;
|
|
1040
|
+
content;
|
|
1041
|
+
args;
|
|
1042
|
+
data;
|
|
1043
|
+
constructor(type, content, args, data) {
|
|
1044
|
+
this.type = type;
|
|
1045
|
+
this.content = content;
|
|
1046
|
+
this.args = args;
|
|
1047
|
+
this.data = data;
|
|
1048
|
+
}
|
|
1049
|
+
eq_shallow(other) {
|
|
1050
|
+
return this.type === other.type && this.content === other.content;
|
|
1051
|
+
}
|
|
1052
|
+
toString() {
|
|
1053
|
+
switch (this.type) {
|
|
1054
|
+
case "text":
|
|
1055
|
+
return `\\text{${this.content}}`;
|
|
1056
|
+
default:
|
|
1057
|
+
throw new Error(`toString() is not implemented for type ${this.type}`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
class TypstToken {
|
|
1062
|
+
type;
|
|
1063
|
+
content;
|
|
1064
|
+
constructor(type, content) {
|
|
1065
|
+
this.type = type;
|
|
1066
|
+
this.content = content;
|
|
1067
|
+
}
|
|
1068
|
+
eq(other) {
|
|
1069
|
+
return this.type === other.type && this.content === other.content;
|
|
1070
|
+
}
|
|
1071
|
+
isOneOf(tokens) {
|
|
1072
|
+
let found = false;
|
|
1073
|
+
for (const token of tokens) {
|
|
1074
|
+
if (this.eq(token)) {
|
|
1075
|
+
found = true;
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return found;
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
class TypstNode {
|
|
1084
|
+
type;
|
|
1085
|
+
content;
|
|
1086
|
+
args;
|
|
1087
|
+
data;
|
|
1088
|
+
options;
|
|
1089
|
+
constructor(type, content, args, data) {
|
|
1090
|
+
this.type = type;
|
|
1091
|
+
this.content = content;
|
|
1092
|
+
this.args = args;
|
|
1093
|
+
this.data = data;
|
|
1094
|
+
}
|
|
1095
|
+
setOptions(options) {
|
|
1096
|
+
this.options = options;
|
|
1097
|
+
}
|
|
1098
|
+
eq_shallow(other) {
|
|
1099
|
+
return this.type === other.type && this.content === other.content;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// src/tex-parser.ts
|
|
1038
1104
|
function assert(condition, message = "") {
|
|
1039
1105
|
if (!condition) {
|
|
1040
1106
|
throw new LatexParserError(message);
|
|
@@ -1091,7 +1157,7 @@ function isdigit(char) {
|
|
|
1091
1157
|
}
|
|
1092
1158
|
function eat_whitespaces(tokens, start) {
|
|
1093
1159
|
let pos = start;
|
|
1094
|
-
while (pos < tokens.length && [4 /*
|
|
1160
|
+
while (pos < tokens.length && [4 /* SPACE */, 5 /* NEWLINE */].includes(tokens[pos].type)) {
|
|
1095
1161
|
pos++;
|
|
1096
1162
|
}
|
|
1097
1163
|
return tokens.slice(start, pos);
|
|
@@ -1108,7 +1174,7 @@ function eat_parenthesis(tokens, start) {
|
|
|
1108
1174
|
}
|
|
1109
1175
|
function eat_primes(tokens, start) {
|
|
1110
1176
|
let pos = start;
|
|
1111
|
-
while (pos < tokens.length && tokens[pos].eq(new
|
|
1177
|
+
while (pos < tokens.length && tokens[pos].eq(new TexToken(0 /* ELEMENT */, "'"))) {
|
|
1112
1178
|
pos += 1;
|
|
1113
1179
|
}
|
|
1114
1180
|
return pos - start;
|
|
@@ -1185,7 +1251,7 @@ function tokenize(latex) {
|
|
|
1185
1251
|
while (newPos < latex.length && latex[newPos] !== "\n") {
|
|
1186
1252
|
newPos += 1;
|
|
1187
1253
|
}
|
|
1188
|
-
token = new
|
|
1254
|
+
token = new TexToken(3 /* COMMENT */, latex.slice(pos + 1, newPos));
|
|
1189
1255
|
pos = newPos;
|
|
1190
1256
|
break;
|
|
1191
1257
|
}
|
|
@@ -1194,19 +1260,19 @@ function tokenize(latex) {
|
|
|
1194
1260
|
case "_":
|
|
1195
1261
|
case "^":
|
|
1196
1262
|
case "&":
|
|
1197
|
-
token = new
|
|
1263
|
+
token = new TexToken(6 /* CONTROL */, firstChar);
|
|
1198
1264
|
pos++;
|
|
1199
1265
|
break;
|
|
1200
1266
|
case "\n":
|
|
1201
|
-
token = new
|
|
1267
|
+
token = new TexToken(5 /* NEWLINE */, firstChar);
|
|
1202
1268
|
pos++;
|
|
1203
1269
|
break;
|
|
1204
1270
|
case "\r": {
|
|
1205
1271
|
if (pos + 1 < latex.length && latex[pos + 1] === "\n") {
|
|
1206
|
-
token = new
|
|
1272
|
+
token = new TexToken(5 /* NEWLINE */, "\n");
|
|
1207
1273
|
pos += 2;
|
|
1208
1274
|
} else {
|
|
1209
|
-
token = new
|
|
1275
|
+
token = new TexToken(5 /* NEWLINE */, "\n");
|
|
1210
1276
|
pos++;
|
|
1211
1277
|
}
|
|
1212
1278
|
break;
|
|
@@ -1216,7 +1282,7 @@ function tokenize(latex) {
|
|
|
1216
1282
|
while (newPos < latex.length && latex[newPos] === " ") {
|
|
1217
1283
|
newPos += 1;
|
|
1218
1284
|
}
|
|
1219
|
-
token = new
|
|
1285
|
+
token = new TexToken(4 /* SPACE */, latex.slice(pos, newPos));
|
|
1220
1286
|
pos = newPos;
|
|
1221
1287
|
break;
|
|
1222
1288
|
}
|
|
@@ -1226,12 +1292,12 @@ function tokenize(latex) {
|
|
|
1226
1292
|
}
|
|
1227
1293
|
const firstTwoChars = latex.slice(pos, pos + 2);
|
|
1228
1294
|
if (["\\\\", "\\,"].includes(firstTwoChars)) {
|
|
1229
|
-
token = new
|
|
1230
|
-
} else if (["\\{", "\\}", "\\%", "\\$", "\\&", "\\#", "\\_"].includes(firstTwoChars)) {
|
|
1231
|
-
token = new
|
|
1295
|
+
token = new TexToken(6 /* CONTROL */, firstTwoChars);
|
|
1296
|
+
} else if (["\\{", "\\}", "\\%", "\\$", "\\&", "\\#", "\\_", "\\|"].includes(firstTwoChars)) {
|
|
1297
|
+
token = new TexToken(0 /* ELEMENT */, firstTwoChars);
|
|
1232
1298
|
} else {
|
|
1233
1299
|
const command = eat_command_name(latex, pos + 1);
|
|
1234
|
-
token = new
|
|
1300
|
+
token = new TexToken(1 /* COMMAND */, "\\" + command);
|
|
1235
1301
|
}
|
|
1236
1302
|
pos += token.value.length;
|
|
1237
1303
|
break;
|
|
@@ -1242,13 +1308,13 @@ function tokenize(latex) {
|
|
|
1242
1308
|
while (newPos < latex.length && isdigit(latex[newPos])) {
|
|
1243
1309
|
newPos += 1;
|
|
1244
1310
|
}
|
|
1245
|
-
token = new
|
|
1311
|
+
token = new TexToken(0 /* ELEMENT */, latex.slice(pos, newPos));
|
|
1246
1312
|
} else if (isalpha(firstChar)) {
|
|
1247
|
-
token = new
|
|
1313
|
+
token = new TexToken(0 /* ELEMENT */, firstChar);
|
|
1248
1314
|
} else if ("+-*/=\'<>!.,;?()[]|".includes(firstChar)) {
|
|
1249
|
-
token = new
|
|
1315
|
+
token = new TexToken(0 /* ELEMENT */, firstChar);
|
|
1250
1316
|
} else {
|
|
1251
|
-
token = new
|
|
1317
|
+
token = new TexToken(7 /* UNKNOWN */, firstChar);
|
|
1252
1318
|
}
|
|
1253
1319
|
pos += token.value.length;
|
|
1254
1320
|
}
|
|
@@ -1258,7 +1324,7 @@ function tokenize(latex) {
|
|
|
1258
1324
|
if (pos >= latex.length || latex[pos] !== "{") {
|
|
1259
1325
|
throw new LatexParserError(`No content for ${token.value} command`);
|
|
1260
1326
|
}
|
|
1261
|
-
tokens.push(new
|
|
1327
|
+
tokens.push(new TexToken(6 /* CONTROL */, "{"));
|
|
1262
1328
|
const posClosingBracket = find_closing_curly_bracket_char(latex, pos);
|
|
1263
1329
|
pos++;
|
|
1264
1330
|
let textInside = latex.slice(pos, posClosingBracket);
|
|
@@ -1266,8 +1332,8 @@ function tokenize(latex) {
|
|
|
1266
1332
|
for (const char of chars) {
|
|
1267
1333
|
textInside = textInside.replaceAll("\\" + char, char);
|
|
1268
1334
|
}
|
|
1269
|
-
tokens.push(new
|
|
1270
|
-
tokens.push(new
|
|
1335
|
+
tokens.push(new TexToken(2 /* TEXT */, textInside));
|
|
1336
|
+
tokens.push(new TexToken(6 /* CONTROL */, "}"));
|
|
1271
1337
|
pos = posClosingBracket + 1;
|
|
1272
1338
|
}
|
|
1273
1339
|
}
|
|
@@ -1277,10 +1343,10 @@ function passIgnoreWhitespaceBeforeScriptMark(tokens) {
|
|
|
1277
1343
|
const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
|
|
1278
1344
|
let out_tokens = [];
|
|
1279
1345
|
for (let i = 0;i < tokens.length; i++) {
|
|
1280
|
-
if (tokens[i].type === 4 /*
|
|
1346
|
+
if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
|
|
1281
1347
|
continue;
|
|
1282
1348
|
}
|
|
1283
|
-
if (tokens[i].type === 4 /*
|
|
1349
|
+
if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
|
|
1284
1350
|
continue;
|
|
1285
1351
|
}
|
|
1286
1352
|
out_tokens.push(tokens[i]);
|
|
@@ -1342,10 +1408,11 @@ var BINARY_COMMANDS = [
|
|
|
1342
1408
|
"binom",
|
|
1343
1409
|
"dbinom",
|
|
1344
1410
|
"dfrac",
|
|
1345
|
-
"tbinom"
|
|
1411
|
+
"tbinom",
|
|
1412
|
+
"overset"
|
|
1346
1413
|
];
|
|
1347
1414
|
|
|
1348
|
-
class
|
|
1415
|
+
class TexToken {
|
|
1349
1416
|
type;
|
|
1350
1417
|
value;
|
|
1351
1418
|
constructor(type, value) {
|
|
@@ -1356,15 +1423,15 @@ class Token {
|
|
|
1356
1423
|
return this.type === token.type && this.value === token.value;
|
|
1357
1424
|
}
|
|
1358
1425
|
}
|
|
1359
|
-
var EMPTY_NODE =
|
|
1360
|
-
var LEFT_CURLY_BRACKET = new
|
|
1361
|
-
var RIGHT_CURLY_BRACKET = new
|
|
1362
|
-
var LEFT_SQUARE_BRACKET = new
|
|
1363
|
-
var RIGHT_SQUARE_BRACKET = new
|
|
1364
|
-
var LEFT_COMMAND = new
|
|
1365
|
-
var RIGHT_COMMAND = new
|
|
1366
|
-
var BEGIN_COMMAND = new
|
|
1367
|
-
var END_COMMAND = new
|
|
1426
|
+
var EMPTY_NODE = new TexNode("empty", "");
|
|
1427
|
+
var LEFT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "{");
|
|
1428
|
+
var RIGHT_CURLY_BRACKET = new TexToken(6 /* CONTROL */, "}");
|
|
1429
|
+
var LEFT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "[");
|
|
1430
|
+
var RIGHT_SQUARE_BRACKET = new TexToken(0 /* ELEMENT */, "]");
|
|
1431
|
+
var LEFT_COMMAND = new TexToken(1 /* COMMAND */, "\\left");
|
|
1432
|
+
var RIGHT_COMMAND = new TexToken(1 /* COMMAND */, "\\right");
|
|
1433
|
+
var BEGIN_COMMAND = new TexToken(1 /* COMMAND */, "\\begin");
|
|
1434
|
+
var END_COMMAND = new TexToken(1 /* COMMAND */, "\\end");
|
|
1368
1435
|
|
|
1369
1436
|
class LatexParserError extends Error {
|
|
1370
1437
|
constructor(message) {
|
|
@@ -1372,8 +1439,8 @@ class LatexParserError extends Error {
|
|
|
1372
1439
|
this.name = "LatexParserError";
|
|
1373
1440
|
}
|
|
1374
1441
|
}
|
|
1375
|
-
var SUB_SYMBOL = new
|
|
1376
|
-
var SUP_SYMBOL = new
|
|
1442
|
+
var SUB_SYMBOL = new TexToken(6 /* CONTROL */, "_");
|
|
1443
|
+
var SUP_SYMBOL = new TexToken(6 /* CONTROL */, "^");
|
|
1377
1444
|
|
|
1378
1445
|
class LatexParser {
|
|
1379
1446
|
space_sensitive;
|
|
@@ -1391,11 +1458,13 @@ class LatexParser {
|
|
|
1391
1458
|
while (pos2 < tokens.length) {
|
|
1392
1459
|
const [res, newPos] = this.parseNextExpr(tokens, pos2);
|
|
1393
1460
|
pos2 = newPos;
|
|
1394
|
-
if (
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1461
|
+
if (res.type === "whitespace") {
|
|
1462
|
+
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
if (!this.newline_sensitive && res.content === "\n") {
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1399
1468
|
}
|
|
1400
1469
|
if (res.type === "control" && res.content === "&") {
|
|
1401
1470
|
throw new LatexParserError("Unexpected & outside of an alignment");
|
|
@@ -1407,7 +1476,7 @@ class LatexParser {
|
|
|
1407
1476
|
} else if (results2.length === 1) {
|
|
1408
1477
|
return results2[0];
|
|
1409
1478
|
} else {
|
|
1410
|
-
return
|
|
1479
|
+
return new TexNode("ordgroup", "", results2);
|
|
1411
1480
|
}
|
|
1412
1481
|
}
|
|
1413
1482
|
if (results.length === 0) {
|
|
@@ -1415,7 +1484,7 @@ class LatexParser {
|
|
|
1415
1484
|
} else if (results.length === 1) {
|
|
1416
1485
|
return results[0];
|
|
1417
1486
|
} else {
|
|
1418
|
-
return
|
|
1487
|
+
return new TexNode("ordgroup", "", results);
|
|
1419
1488
|
}
|
|
1420
1489
|
}
|
|
1421
1490
|
parseNextExpr(tokens, start) {
|
|
@@ -1453,9 +1522,9 @@ class LatexParser {
|
|
|
1453
1522
|
res.sub = sub;
|
|
1454
1523
|
}
|
|
1455
1524
|
if (num_prime > 0) {
|
|
1456
|
-
res.sup =
|
|
1525
|
+
res.sup = new TexNode("ordgroup", "", []);
|
|
1457
1526
|
for (let i = 0;i < num_prime; i++) {
|
|
1458
|
-
res.sup.args.push(
|
|
1527
|
+
res.sup.args.push(new TexNode("element", "'"));
|
|
1459
1528
|
}
|
|
1460
1529
|
if (sup) {
|
|
1461
1530
|
res.sup.args.push(sup);
|
|
@@ -1466,7 +1535,7 @@ class LatexParser {
|
|
|
1466
1535
|
} else if (sup) {
|
|
1467
1536
|
res.sup = sup;
|
|
1468
1537
|
}
|
|
1469
|
-
return [
|
|
1538
|
+
return [new TexNode("supsub", "", [], res), pos];
|
|
1470
1539
|
} else {
|
|
1471
1540
|
return [base, pos];
|
|
1472
1541
|
}
|
|
@@ -1476,15 +1545,14 @@ class LatexParser {
|
|
|
1476
1545
|
const tokenType = firstToken.type;
|
|
1477
1546
|
switch (tokenType) {
|
|
1478
1547
|
case 0 /* ELEMENT */:
|
|
1479
|
-
return [
|
|
1548
|
+
return [new TexNode("element", firstToken.value), start + 1];
|
|
1480
1549
|
case 2 /* TEXT */:
|
|
1481
|
-
return [
|
|
1550
|
+
return [new TexNode("text", firstToken.value), start + 1];
|
|
1482
1551
|
case 3 /* COMMENT */:
|
|
1483
|
-
return [
|
|
1484
|
-
case 4 /*
|
|
1485
|
-
return [{ type: "whitespace", content: firstToken.value }, start + 1];
|
|
1552
|
+
return [new TexNode("comment", firstToken.value), start + 1];
|
|
1553
|
+
case 4 /* SPACE */:
|
|
1486
1554
|
case 5 /* NEWLINE */:
|
|
1487
|
-
return [
|
|
1555
|
+
return [new TexNode("whitespace", firstToken.value), start + 1];
|
|
1488
1556
|
case 1 /* COMMAND */:
|
|
1489
1557
|
if (firstToken.eq(BEGIN_COMMAND)) {
|
|
1490
1558
|
return this.parseBeginEndExpr(tokens, start);
|
|
@@ -1503,9 +1571,9 @@ class LatexParser {
|
|
|
1503
1571
|
case "}":
|
|
1504
1572
|
throw new LatexParserError("Unmatched '}'");
|
|
1505
1573
|
case "\\\\":
|
|
1506
|
-
return [
|
|
1574
|
+
return [new TexNode("control", "\\\\"), start + 1];
|
|
1507
1575
|
case "\\,":
|
|
1508
|
-
return [
|
|
1576
|
+
return [new TexNode("control", "\\,"), start + 1];
|
|
1509
1577
|
case "_": {
|
|
1510
1578
|
return [EMPTY_NODE, start];
|
|
1511
1579
|
}
|
|
@@ -1513,7 +1581,7 @@ class LatexParser {
|
|
|
1513
1581
|
return [EMPTY_NODE, start];
|
|
1514
1582
|
}
|
|
1515
1583
|
case "&":
|
|
1516
|
-
return [
|
|
1584
|
+
return [new TexNode("control", "&"), start + 1];
|
|
1517
1585
|
default:
|
|
1518
1586
|
throw new LatexParserError("Unknown control sequence");
|
|
1519
1587
|
}
|
|
@@ -1532,9 +1600,9 @@ class LatexParser {
|
|
|
1532
1600
|
switch (paramNum) {
|
|
1533
1601
|
case 0:
|
|
1534
1602
|
if (!symbolMap.has(command.slice(1))) {
|
|
1535
|
-
return [
|
|
1603
|
+
return [new TexNode("unknownMacro", command), pos];
|
|
1536
1604
|
}
|
|
1537
|
-
return [
|
|
1605
|
+
return [new TexNode("symbol", command), pos];
|
|
1538
1606
|
case 1: {
|
|
1539
1607
|
if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
|
|
1540
1608
|
const posLeftSquareBracket = pos;
|
|
@@ -1542,7 +1610,7 @@ class LatexParser {
|
|
|
1542
1610
|
const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
|
|
1543
1611
|
const exponent = this.parse(exprInside);
|
|
1544
1612
|
const [arg12, newPos2] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
|
|
1545
|
-
return [
|
|
1613
|
+
return [new TexNode("unaryFunc", command, [arg12], exponent), newPos2];
|
|
1546
1614
|
} else if (command === "\\text") {
|
|
1547
1615
|
if (pos + 2 >= tokens.length) {
|
|
1548
1616
|
throw new LatexParserError("Expecting content for \\text command");
|
|
@@ -1551,15 +1619,15 @@ class LatexParser {
|
|
|
1551
1619
|
assert(tokens[pos + 1].type === 2 /* TEXT */);
|
|
1552
1620
|
assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
|
|
1553
1621
|
const text = tokens[pos + 1].value;
|
|
1554
|
-
return [
|
|
1622
|
+
return [new TexNode("text", text), pos + 3];
|
|
1555
1623
|
}
|
|
1556
1624
|
let [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, pos);
|
|
1557
|
-
return [
|
|
1625
|
+
return [new TexNode("unaryFunc", command, [arg1]), newPos];
|
|
1558
1626
|
}
|
|
1559
1627
|
case 2: {
|
|
1560
1628
|
const [arg1, pos1] = this.parseNextExprWithoutSupSub(tokens, pos);
|
|
1561
1629
|
const [arg2, pos2] = this.parseNextExprWithoutSupSub(tokens, pos1);
|
|
1562
|
-
return [
|
|
1630
|
+
return [new TexNode("binaryFunc", command, [arg1, arg2]), pos2];
|
|
1563
1631
|
}
|
|
1564
1632
|
default:
|
|
1565
1633
|
throw new Error("Invalid number of parameters");
|
|
@@ -1596,11 +1664,11 @@ class LatexParser {
|
|
|
1596
1664
|
const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
|
|
1597
1665
|
const body = this.parse(exprInside);
|
|
1598
1666
|
const args = [
|
|
1599
|
-
|
|
1667
|
+
new TexNode("element", leftDelimiter.value),
|
|
1600
1668
|
body,
|
|
1601
|
-
|
|
1669
|
+
new TexNode("element", rightDelimiter.value)
|
|
1602
1670
|
];
|
|
1603
|
-
const res =
|
|
1671
|
+
const res = new TexNode("leftright", "", args);
|
|
1604
1672
|
return [res, pos];
|
|
1605
1673
|
}
|
|
1606
1674
|
parseBeginEndExpr(tokens, start) {
|
|
@@ -1627,11 +1695,11 @@ class LatexParser {
|
|
|
1627
1695
|
}
|
|
1628
1696
|
pos += 3;
|
|
1629
1697
|
const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
|
|
1630
|
-
while (exprInside.length > 0 && [4 /*
|
|
1698
|
+
while (exprInside.length > 0 && [4 /* SPACE */, 5 /* NEWLINE */].includes(exprInside[exprInside.length - 1].type)) {
|
|
1631
1699
|
exprInside.pop();
|
|
1632
1700
|
}
|
|
1633
1701
|
const body = this.parseAligned(exprInside);
|
|
1634
|
-
const res =
|
|
1702
|
+
const res = new TexNode("beginend", envName, [], body);
|
|
1635
1703
|
return [res, pos];
|
|
1636
1704
|
}
|
|
1637
1705
|
parseAligned(tokens) {
|
|
@@ -1639,22 +1707,26 @@ class LatexParser {
|
|
|
1639
1707
|
const allRows = [];
|
|
1640
1708
|
let row = [];
|
|
1641
1709
|
allRows.push(row);
|
|
1642
|
-
let group =
|
|
1710
|
+
let group = new TexNode("ordgroup", "", []);
|
|
1643
1711
|
row.push(group);
|
|
1644
1712
|
while (pos < tokens.length) {
|
|
1645
1713
|
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
1646
1714
|
pos = newPos;
|
|
1647
1715
|
if (res.type === "whitespace") {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1716
|
+
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
if (!this.newline_sensitive && res.content === "\n") {
|
|
1720
|
+
continue;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (res.type === "control" && res.content === "\\\\") {
|
|
1652
1724
|
row = [];
|
|
1653
|
-
group =
|
|
1725
|
+
group = new TexNode("ordgroup", "", []);
|
|
1654
1726
|
row.push(group);
|
|
1655
1727
|
allRows.push(row);
|
|
1656
1728
|
} else if (res.type === "control" && res.content === "&") {
|
|
1657
|
-
group =
|
|
1729
|
+
group = new TexNode("ordgroup", "", []);
|
|
1658
1730
|
row.push(group);
|
|
1659
1731
|
} else {
|
|
1660
1732
|
group.args.push(res);
|
|
@@ -1668,45 +1740,62 @@ class LatexParser {
|
|
|
1668
1740
|
function is_delimiter(c) {
|
|
1669
1741
|
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
|
|
1670
1742
|
}
|
|
1743
|
+
function convert_overset(node) {
|
|
1744
|
+
const [sup, base] = node.args;
|
|
1745
|
+
const is_def = (n) => {
|
|
1746
|
+
if (n.eq_shallow(new TexNode("text", "def"))) {
|
|
1747
|
+
return true;
|
|
1748
|
+
}
|
|
1749
|
+
if (n.type === "ordgroup" && n.args.length === 3) {
|
|
1750
|
+
const [a1, a2, a3] = n.args;
|
|
1751
|
+
const d = new TexNode("element", "d");
|
|
1752
|
+
const e = new TexNode("element", "e");
|
|
1753
|
+
const f = new TexNode("element", "f");
|
|
1754
|
+
if (a1.eq_shallow(d) && a2.eq_shallow(e) && a3.eq_shallow(f)) {
|
|
1755
|
+
return true;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
return false;
|
|
1759
|
+
};
|
|
1760
|
+
const is_eq = (n) => n.eq_shallow(new TexNode("element", "="));
|
|
1761
|
+
if (is_def(sup) && is_eq(base)) {
|
|
1762
|
+
return new TypstNode("symbol", "eq.def");
|
|
1763
|
+
}
|
|
1764
|
+
const op_call = new TypstNode("unaryFunc", "op", [convertTree(base)]);
|
|
1765
|
+
op_call.setOptions({ limits: "#true" });
|
|
1766
|
+
return new TypstNode("supsub", "", [], {
|
|
1767
|
+
base: op_call,
|
|
1768
|
+
sup: convertTree(sup)
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1671
1771
|
function convertTree(node) {
|
|
1672
1772
|
switch (node.type) {
|
|
1673
1773
|
case "empty":
|
|
1774
|
+
return new TypstNode("empty", "");
|
|
1674
1775
|
case "whitespace":
|
|
1675
|
-
return
|
|
1776
|
+
return new TypstNode("whitespace", node.content);
|
|
1676
1777
|
case "ordgroup":
|
|
1677
|
-
return
|
|
1678
|
-
type: "group",
|
|
1679
|
-
content: "",
|
|
1680
|
-
args: node.args.map(convertTree)
|
|
1681
|
-
};
|
|
1778
|
+
return new TypstNode("group", "", node.args.map(convertTree));
|
|
1682
1779
|
case "element":
|
|
1683
|
-
return
|
|
1780
|
+
return new TypstNode("atom", convertToken(node.content));
|
|
1684
1781
|
case "symbol":
|
|
1685
|
-
return
|
|
1782
|
+
return new TypstNode("symbol", convertToken(node.content));
|
|
1686
1783
|
case "text":
|
|
1687
|
-
return
|
|
1784
|
+
return new TypstNode("text", node.content);
|
|
1688
1785
|
case "comment":
|
|
1689
|
-
return
|
|
1786
|
+
return new TypstNode("comment", node.content);
|
|
1690
1787
|
case "supsub": {
|
|
1691
1788
|
let { base, sup, sub } = node.data;
|
|
1692
1789
|
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
1693
|
-
return
|
|
1694
|
-
type: "binaryFunc",
|
|
1695
|
-
content: "overbrace",
|
|
1696
|
-
args: [convertTree(base.args[0]), convertTree(sup)]
|
|
1697
|
-
};
|
|
1790
|
+
return new TypstNode("binaryFunc", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
|
|
1698
1791
|
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
1699
|
-
return
|
|
1700
|
-
type: "binaryFunc",
|
|
1701
|
-
content: "underbrace",
|
|
1702
|
-
args: [convertTree(base.args[0]), convertTree(sub)]
|
|
1703
|
-
};
|
|
1792
|
+
return new TypstNode("binaryFunc", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
|
|
1704
1793
|
}
|
|
1705
1794
|
const data = {
|
|
1706
1795
|
base: convertTree(base)
|
|
1707
1796
|
};
|
|
1708
1797
|
if (data.base.type === "empty") {
|
|
1709
|
-
data.base =
|
|
1798
|
+
data.base = new TypstNode("text", "");
|
|
1710
1799
|
}
|
|
1711
1800
|
if (sup) {
|
|
1712
1801
|
data.sup = convertTree(sup);
|
|
@@ -1714,19 +1803,11 @@ function convertTree(node) {
|
|
|
1714
1803
|
if (sub) {
|
|
1715
1804
|
data.sub = convertTree(sub);
|
|
1716
1805
|
}
|
|
1717
|
-
return
|
|
1718
|
-
type: "supsub",
|
|
1719
|
-
content: "",
|
|
1720
|
-
data
|
|
1721
|
-
};
|
|
1806
|
+
return new TypstNode("supsub", "", [], data);
|
|
1722
1807
|
}
|
|
1723
1808
|
case "leftright": {
|
|
1724
1809
|
const [left, body, right] = node.args;
|
|
1725
|
-
const group =
|
|
1726
|
-
type: "group",
|
|
1727
|
-
content: "",
|
|
1728
|
-
args: node.args.map(convertTree)
|
|
1729
|
-
};
|
|
1810
|
+
const group = new TypstNode("group", "", node.args.map(convertTree));
|
|
1730
1811
|
if ([
|
|
1731
1812
|
"[]",
|
|
1732
1813
|
"()",
|
|
@@ -1737,46 +1818,26 @@ function convertTree(node) {
|
|
|
1737
1818
|
].includes(left.content + right.content)) {
|
|
1738
1819
|
return group;
|
|
1739
1820
|
}
|
|
1740
|
-
return
|
|
1741
|
-
type: "unaryFunc",
|
|
1742
|
-
content: "lr",
|
|
1743
|
-
args: [group]
|
|
1744
|
-
};
|
|
1821
|
+
return new TypstNode("unaryFunc", "lr", [group]);
|
|
1745
1822
|
}
|
|
1746
1823
|
case "binaryFunc": {
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
};
|
|
1824
|
+
if (node.content === "\\overset") {
|
|
1825
|
+
return convert_overset(node);
|
|
1826
|
+
}
|
|
1827
|
+
return new TypstNode("binaryFunc", convertToken(node.content), node.args.map(convertTree));
|
|
1752
1828
|
}
|
|
1753
1829
|
case "unaryFunc": {
|
|
1754
1830
|
const arg0 = convertTree(node.args[0]);
|
|
1755
1831
|
if (node.content === "\\sqrt" && node.data) {
|
|
1756
1832
|
const data = convertTree(node.data);
|
|
1757
|
-
return
|
|
1758
|
-
type: "binaryFunc",
|
|
1759
|
-
content: "root",
|
|
1760
|
-
args: [data, arg0]
|
|
1761
|
-
};
|
|
1833
|
+
return new TypstNode("binaryFunc", "root", [data, arg0]);
|
|
1762
1834
|
}
|
|
1763
1835
|
if (node.content === "\\mathbf") {
|
|
1764
|
-
const inner =
|
|
1765
|
-
|
|
1766
|
-
content: "bold",
|
|
1767
|
-
args: [arg0]
|
|
1768
|
-
};
|
|
1769
|
-
return {
|
|
1770
|
-
type: "unaryFunc",
|
|
1771
|
-
content: "upright",
|
|
1772
|
-
args: [inner]
|
|
1773
|
-
};
|
|
1836
|
+
const inner = new TypstNode("unaryFunc", "bold", [arg0]);
|
|
1837
|
+
return new TypstNode("unaryFunc", "upright", [inner]);
|
|
1774
1838
|
}
|
|
1775
1839
|
if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
|
|
1776
|
-
return
|
|
1777
|
-
type: "symbol",
|
|
1778
|
-
content: arg0.content + arg0.content
|
|
1779
|
-
};
|
|
1840
|
+
return new TypstNode("symbol", arg0.content + arg0.content);
|
|
1780
1841
|
}
|
|
1781
1842
|
if (node.content === "\\operatorname") {
|
|
1782
1843
|
const body = node.args;
|
|
@@ -1785,50 +1846,31 @@ function convertTree(node) {
|
|
|
1785
1846
|
}
|
|
1786
1847
|
const text = body[0].content;
|
|
1787
1848
|
if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
|
|
1788
|
-
return
|
|
1789
|
-
type: "symbol",
|
|
1790
|
-
content: text
|
|
1791
|
-
};
|
|
1849
|
+
return new TypstNode("symbol", text);
|
|
1792
1850
|
} else {
|
|
1793
|
-
return
|
|
1794
|
-
type: "unaryFunc",
|
|
1795
|
-
content: "op",
|
|
1796
|
-
args: [{ type: "text", content: text }]
|
|
1797
|
-
};
|
|
1851
|
+
return new TypstNode("unaryFunc", "op", [new TypstNode("text", text)]);
|
|
1798
1852
|
}
|
|
1799
1853
|
}
|
|
1800
|
-
return
|
|
1801
|
-
type: "unaryFunc",
|
|
1802
|
-
content: convertToken(node.content),
|
|
1803
|
-
args: node.args.map(convertTree)
|
|
1804
|
-
};
|
|
1854
|
+
return new TypstNode("unaryFunc", convertToken(node.content), node.args.map(convertTree));
|
|
1805
1855
|
}
|
|
1806
|
-
case "newline":
|
|
1807
|
-
return { type: "newline", content: "\n" };
|
|
1808
1856
|
case "beginend": {
|
|
1809
1857
|
const matrix = node.data;
|
|
1810
1858
|
const data = matrix.map((row) => row.map(convertTree));
|
|
1811
1859
|
if (node.content.startsWith("align")) {
|
|
1812
|
-
return
|
|
1813
|
-
type: "align",
|
|
1814
|
-
content: "",
|
|
1815
|
-
data
|
|
1816
|
-
};
|
|
1860
|
+
return new TypstNode("align", "", [], data);
|
|
1817
1861
|
} else {
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
data
|
|
1822
|
-
};
|
|
1862
|
+
const res = new TypstNode("matrix", "", [], data);
|
|
1863
|
+
res.setOptions({ delim: "#none" });
|
|
1864
|
+
return res;
|
|
1823
1865
|
}
|
|
1824
1866
|
}
|
|
1825
1867
|
case "unknownMacro":
|
|
1826
|
-
return
|
|
1868
|
+
return new TypstNode("unknown", convertToken(node.content));
|
|
1827
1869
|
case "control":
|
|
1828
1870
|
if (node.content === "\\\\") {
|
|
1829
|
-
return
|
|
1871
|
+
return new TypstNode("symbol", "\\");
|
|
1830
1872
|
} else if (node.content === "\\,") {
|
|
1831
|
-
return
|
|
1873
|
+
return new TypstNode("symbol", "thin");
|
|
1832
1874
|
} else {
|
|
1833
1875
|
throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
|
|
1834
1876
|
}
|
|
@@ -1839,10 +1881,14 @@ function convertTree(node) {
|
|
|
1839
1881
|
function convertToken(token) {
|
|
1840
1882
|
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
1841
1883
|
return token;
|
|
1884
|
+
} else if (token === "/") {
|
|
1885
|
+
return "\\/";
|
|
1886
|
+
} else if (token === "\\|") {
|
|
1887
|
+
return "parallel";
|
|
1888
|
+
} else if (token === "\\colon") {
|
|
1889
|
+
return ":";
|
|
1842
1890
|
} else if (token === "\\\\") {
|
|
1843
1891
|
return "\\";
|
|
1844
|
-
} else if (token == "/") {
|
|
1845
|
-
return "\\/";
|
|
1846
1892
|
} else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
|
|
1847
1893
|
return token;
|
|
1848
1894
|
} else if (token.startsWith("\\")) {
|
|
@@ -1864,6 +1910,10 @@ var TYPST_INTRINSIC_SYMBOLS = [
|
|
|
1864
1910
|
"sech",
|
|
1865
1911
|
"csch"
|
|
1866
1912
|
];
|
|
1913
|
+
var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ATOM */, "(");
|
|
1914
|
+
var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ATOM */, ")");
|
|
1915
|
+
var TYPST_COMMA = new TypstToken(1 /* ATOM */, ",");
|
|
1916
|
+
var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, "\n");
|
|
1867
1917
|
|
|
1868
1918
|
class TypstWriterError extends Error {
|
|
1869
1919
|
node;
|
|
@@ -1877,57 +1927,72 @@ class TypstWriterError extends Error {
|
|
|
1877
1927
|
class TypstWriter {
|
|
1878
1928
|
nonStrict;
|
|
1879
1929
|
preferTypstIntrinsic;
|
|
1930
|
+
keepSpaces;
|
|
1880
1931
|
buffer = "";
|
|
1881
1932
|
queue = [];
|
|
1882
|
-
needSpaceAfterSingleItemScript = false;
|
|
1883
1933
|
insideFunctionDepth = 0;
|
|
1884
|
-
constructor(nonStrict, preferTypstIntrinsic) {
|
|
1934
|
+
constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
|
|
1885
1935
|
this.nonStrict = nonStrict;
|
|
1886
1936
|
this.preferTypstIntrinsic = preferTypstIntrinsic;
|
|
1937
|
+
this.keepSpaces = keepSpaces;
|
|
1887
1938
|
}
|
|
1888
|
-
writeBuffer(
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
let no_need_space = false;
|
|
1893
|
-
no_need_space ||= /[\(\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
1894
|
-
no_need_space ||= /^[}()_^,;!\|]$/.test(str);
|
|
1895
|
-
no_need_space ||= str === "'";
|
|
1896
|
-
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1897
|
-
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
1898
|
-
no_need_space ||= str.startsWith("\n");
|
|
1899
|
-
no_need_space ||= this.buffer === "";
|
|
1900
|
-
no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
|
|
1901
|
-
if (!no_need_space) {
|
|
1902
|
-
this.buffer += " ";
|
|
1903
|
-
}
|
|
1939
|
+
writeBuffer(token) {
|
|
1940
|
+
const str = token.content;
|
|
1941
|
+
if (str === "") {
|
|
1942
|
+
return;
|
|
1904
1943
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1944
|
+
let no_need_space = false;
|
|
1945
|
+
no_need_space ||= /[\(\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
1946
|
+
no_need_space ||= /^[}()_^,;!\|]$/.test(str);
|
|
1947
|
+
no_need_space ||= str === "'";
|
|
1948
|
+
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1949
|
+
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
1950
|
+
no_need_space ||= str.startsWith("\n");
|
|
1951
|
+
no_need_space ||= this.buffer === "";
|
|
1952
|
+
no_need_space ||= /^\s/.test(str);
|
|
1953
|
+
no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
|
|
1954
|
+
if (!no_need_space) {
|
|
1955
|
+
this.buffer += " ";
|
|
1907
1956
|
}
|
|
1908
1957
|
this.buffer += str;
|
|
1909
1958
|
}
|
|
1910
|
-
|
|
1959
|
+
serialize(node) {
|
|
1911
1960
|
switch (node.type) {
|
|
1912
1961
|
case "empty":
|
|
1913
1962
|
break;
|
|
1914
1963
|
case "atom": {
|
|
1915
1964
|
if (node.content === "," && this.insideFunctionDepth > 0) {
|
|
1916
|
-
this.queue.push(
|
|
1965
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "comma"));
|
|
1917
1966
|
} else {
|
|
1918
|
-
this.queue.push(
|
|
1967
|
+
this.queue.push(new TypstToken(1 /* ATOM */, node.content));
|
|
1919
1968
|
}
|
|
1920
1969
|
break;
|
|
1921
1970
|
}
|
|
1922
1971
|
case "symbol":
|
|
1972
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
|
|
1973
|
+
break;
|
|
1923
1974
|
case "text":
|
|
1975
|
+
this.queue.push(new TypstToken(2 /* TEXT */, `"${node.content}"`));
|
|
1976
|
+
break;
|
|
1924
1977
|
case "comment":
|
|
1925
|
-
|
|
1926
|
-
|
|
1978
|
+
this.queue.push(new TypstToken(3 /* COMMENT */, `//${node.content}`));
|
|
1979
|
+
break;
|
|
1980
|
+
case "whitespace":
|
|
1981
|
+
for (const c of node.content) {
|
|
1982
|
+
if (c === " ") {
|
|
1983
|
+
if (this.keepSpaces) {
|
|
1984
|
+
this.queue.push(new TypstToken(4 /* SPACE */, c));
|
|
1985
|
+
}
|
|
1986
|
+
} else if (c === "\n") {
|
|
1987
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, c));
|
|
1988
|
+
} else {
|
|
1989
|
+
throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1927
1992
|
break;
|
|
1928
1993
|
case "group":
|
|
1929
1994
|
for (const item of node.args) {
|
|
1930
|
-
this.
|
|
1995
|
+
this.serialize(item);
|
|
1931
1996
|
}
|
|
1932
1997
|
break;
|
|
1933
1998
|
case "supsub": {
|
|
@@ -1936,43 +2001,48 @@ class TypstWriter {
|
|
|
1936
2001
|
let trailing_space_needed = false;
|
|
1937
2002
|
const has_prime = sup && sup.type === "atom" && sup.content === "\'";
|
|
1938
2003
|
if (has_prime) {
|
|
1939
|
-
this.queue.push(
|
|
2004
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "\'"));
|
|
1940
2005
|
trailing_space_needed = false;
|
|
1941
2006
|
}
|
|
1942
2007
|
if (sub) {
|
|
1943
|
-
this.queue.push(
|
|
2008
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "_"));
|
|
1944
2009
|
trailing_space_needed = this.appendWithBracketsIfNeeded(sub);
|
|
1945
2010
|
}
|
|
1946
2011
|
if (sup && !has_prime) {
|
|
1947
|
-
this.queue.push(
|
|
2012
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "^"));
|
|
1948
2013
|
trailing_space_needed = this.appendWithBracketsIfNeeded(sup);
|
|
1949
2014
|
}
|
|
1950
2015
|
if (trailing_space_needed) {
|
|
1951
|
-
this.queue.push(
|
|
2016
|
+
this.queue.push(new TypstToken(6 /* CONTROL */, " "));
|
|
1952
2017
|
}
|
|
1953
2018
|
break;
|
|
1954
2019
|
}
|
|
1955
2020
|
case "binaryFunc": {
|
|
1956
|
-
const func_symbol =
|
|
2021
|
+
const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
|
|
1957
2022
|
const [arg0, arg1] = node.args;
|
|
1958
2023
|
this.queue.push(func_symbol);
|
|
1959
2024
|
this.insideFunctionDepth++;
|
|
1960
|
-
this.queue.push(
|
|
1961
|
-
this.
|
|
1962
|
-
this.queue.push(
|
|
1963
|
-
this.
|
|
1964
|
-
this.queue.push(
|
|
2025
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2026
|
+
this.serialize(arg0);
|
|
2027
|
+
this.queue.push(new TypstToken(1 /* ATOM */, ","));
|
|
2028
|
+
this.serialize(arg1);
|
|
2029
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
1965
2030
|
this.insideFunctionDepth--;
|
|
1966
2031
|
break;
|
|
1967
2032
|
}
|
|
1968
2033
|
case "unaryFunc": {
|
|
1969
|
-
const func_symbol =
|
|
2034
|
+
const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
|
|
1970
2035
|
const arg0 = node.args[0];
|
|
1971
2036
|
this.queue.push(func_symbol);
|
|
1972
2037
|
this.insideFunctionDepth++;
|
|
1973
|
-
this.queue.push(
|
|
1974
|
-
this.
|
|
1975
|
-
|
|
2038
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2039
|
+
this.serialize(arg0);
|
|
2040
|
+
if (node.options) {
|
|
2041
|
+
for (const [key, value] of Object.entries(node.options)) {
|
|
2042
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, `, ${key}: ${value}`));
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
1976
2046
|
this.insideFunctionDepth--;
|
|
1977
2047
|
break;
|
|
1978
2048
|
}
|
|
@@ -1981,41 +2051,45 @@ class TypstWriter {
|
|
|
1981
2051
|
matrix.forEach((row, i) => {
|
|
1982
2052
|
row.forEach((cell, j) => {
|
|
1983
2053
|
if (j > 0) {
|
|
1984
|
-
this.queue.push(
|
|
2054
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "&"));
|
|
1985
2055
|
}
|
|
1986
|
-
this.
|
|
2056
|
+
this.serialize(cell);
|
|
1987
2057
|
});
|
|
1988
2058
|
if (i < matrix.length - 1) {
|
|
1989
|
-
this.queue.push(
|
|
2059
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "\\"));
|
|
1990
2060
|
}
|
|
1991
2061
|
});
|
|
1992
2062
|
break;
|
|
1993
2063
|
}
|
|
1994
2064
|
case "matrix": {
|
|
1995
2065
|
const matrix = node.data;
|
|
1996
|
-
this.queue.push(
|
|
2066
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "mat"));
|
|
1997
2067
|
this.insideFunctionDepth++;
|
|
1998
|
-
this.queue.push(
|
|
1999
|
-
|
|
2068
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2069
|
+
if (node.options) {
|
|
2070
|
+
for (const [key, value] of Object.entries(node.options)) {
|
|
2071
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, `${key}: ${value}, `));
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2000
2074
|
matrix.forEach((row, i) => {
|
|
2001
2075
|
row.forEach((cell, j) => {
|
|
2002
|
-
this.
|
|
2076
|
+
this.serialize(cell);
|
|
2003
2077
|
if (j < row.length - 1) {
|
|
2004
|
-
this.queue.push(
|
|
2078
|
+
this.queue.push(new TypstToken(1 /* ATOM */, ","));
|
|
2005
2079
|
} else {
|
|
2006
2080
|
if (i < matrix.length - 1) {
|
|
2007
|
-
this.queue.push(
|
|
2081
|
+
this.queue.push(new TypstToken(1 /* ATOM */, ";"));
|
|
2008
2082
|
}
|
|
2009
2083
|
}
|
|
2010
2084
|
});
|
|
2011
2085
|
});
|
|
2012
|
-
this.queue.push(
|
|
2086
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2013
2087
|
this.insideFunctionDepth--;
|
|
2014
2088
|
break;
|
|
2015
2089
|
}
|
|
2016
2090
|
case "unknown": {
|
|
2017
2091
|
if (this.nonStrict) {
|
|
2018
|
-
this.queue.push(
|
|
2092
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
|
|
2019
2093
|
} else {
|
|
2020
2094
|
throw new TypstWriterError(`Unknown macro: ${node.content}`, node);
|
|
2021
2095
|
}
|
|
@@ -2035,41 +2109,28 @@ class TypstWriter {
|
|
|
2035
2109
|
}
|
|
2036
2110
|
}
|
|
2037
2111
|
if (need_to_wrap) {
|
|
2038
|
-
this.queue.push(
|
|
2039
|
-
this.
|
|
2040
|
-
this.queue.push(
|
|
2112
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2113
|
+
this.serialize(node);
|
|
2114
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2041
2115
|
} else {
|
|
2042
|
-
this.
|
|
2116
|
+
this.serialize(node);
|
|
2043
2117
|
}
|
|
2044
2118
|
return !need_to_wrap;
|
|
2045
2119
|
}
|
|
2046
2120
|
flushQueue() {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
break;
|
|
2057
|
-
case "softSpace":
|
|
2058
|
-
this.needSpaceAfterSingleItemScript = true;
|
|
2059
|
-
str = "";
|
|
2060
|
-
break;
|
|
2061
|
-
case "comment":
|
|
2062
|
-
str = `//${node.content}`;
|
|
2063
|
-
break;
|
|
2064
|
-
case "newline":
|
|
2065
|
-
str = "\n";
|
|
2066
|
-
break;
|
|
2067
|
-
default:
|
|
2068
|
-
throw new TypstWriterError(`Unexpected node type to stringify: ${node.type}`, node);
|
|
2069
|
-
}
|
|
2070
|
-
if (str !== "") {
|
|
2071
|
-
this.writeBuffer(str);
|
|
2121
|
+
const SOFT_SPACE = new TypstToken(6 /* CONTROL */, " ");
|
|
2122
|
+
for (let i = 0;i < this.queue.length; i++) {
|
|
2123
|
+
let token = this.queue[i];
|
|
2124
|
+
if (token.eq(SOFT_SPACE)) {
|
|
2125
|
+
if (i === this.queue.length - 1) {
|
|
2126
|
+
this.queue[i].content = "";
|
|
2127
|
+
} else if (this.queue[i + 1].isOneOf([TYPST_RIGHT_PARENTHESIS, TYPST_COMMA, TYPST_NEWLINE])) {
|
|
2128
|
+
this.queue[i].content = "";
|
|
2129
|
+
}
|
|
2072
2130
|
}
|
|
2131
|
+
}
|
|
2132
|
+
this.queue.forEach((token) => {
|
|
2133
|
+
this.writeBuffer(token);
|
|
2073
2134
|
});
|
|
2074
2135
|
this.queue = [];
|
|
2075
2136
|
}
|
|
@@ -2103,6 +2164,7 @@ function tex2typst(tex, options) {
|
|
|
2103
2164
|
const opt = {
|
|
2104
2165
|
nonStrict: true,
|
|
2105
2166
|
preferTypstIntrinsic: true,
|
|
2167
|
+
keepSpaces: false,
|
|
2106
2168
|
customTexMacros: {}
|
|
2107
2169
|
};
|
|
2108
2170
|
if (options) {
|
|
@@ -2118,8 +2180,8 @@ function tex2typst(tex, options) {
|
|
|
2118
2180
|
}
|
|
2119
2181
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
2120
2182
|
const typstTree = convertTree(texTree);
|
|
2121
|
-
const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic);
|
|
2122
|
-
writer2.
|
|
2183
|
+
const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
|
|
2184
|
+
writer2.serialize(typstTree);
|
|
2123
2185
|
return writer2.finalize();
|
|
2124
2186
|
}
|
|
2125
2187
|
export {
|