tex2typst 0.2.13 → 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 +278 -269
- package/dist/tex-parser.d.ts +28 -0
- package/dist/tex2typst.min.js +1 -1
- package/dist/types.d.ts +44 -6
- package/dist/writer.d.ts +7 -7
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/{parser.ts → tex-parser.ts} +117 -110
- package/src/types.ts +97 -10
- package/src/writer.ts +224 -225
- 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
|
|
1295
|
+
token = new TexToken(6 /* CONTROL */, firstTwoChars);
|
|
1230
1296
|
} else if (["\\{", "\\}", "\\%", "\\$", "\\&", "\\#", "\\_", "\\|"].includes(firstTwoChars)) {
|
|
1231
|
-
token = new
|
|
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]);
|
|
@@ -1346,7 +1412,7 @@ var BINARY_COMMANDS = [
|
|
|
1346
1412
|
"overset"
|
|
1347
1413
|
];
|
|
1348
1414
|
|
|
1349
|
-
class
|
|
1415
|
+
class TexToken {
|
|
1350
1416
|
type;
|
|
1351
1417
|
value;
|
|
1352
1418
|
constructor(type, value) {
|
|
@@ -1357,15 +1423,15 @@ class Token {
|
|
|
1357
1423
|
return this.type === token.type && this.value === token.value;
|
|
1358
1424
|
}
|
|
1359
1425
|
}
|
|
1360
|
-
var EMPTY_NODE =
|
|
1361
|
-
var LEFT_CURLY_BRACKET = new
|
|
1362
|
-
var RIGHT_CURLY_BRACKET = new
|
|
1363
|
-
var LEFT_SQUARE_BRACKET = new
|
|
1364
|
-
var RIGHT_SQUARE_BRACKET = new
|
|
1365
|
-
var LEFT_COMMAND = new
|
|
1366
|
-
var RIGHT_COMMAND = new
|
|
1367
|
-
var BEGIN_COMMAND = new
|
|
1368
|
-
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");
|
|
1369
1435
|
|
|
1370
1436
|
class LatexParserError extends Error {
|
|
1371
1437
|
constructor(message) {
|
|
@@ -1373,8 +1439,8 @@ class LatexParserError extends Error {
|
|
|
1373
1439
|
this.name = "LatexParserError";
|
|
1374
1440
|
}
|
|
1375
1441
|
}
|
|
1376
|
-
var SUB_SYMBOL = new
|
|
1377
|
-
var SUP_SYMBOL = new
|
|
1442
|
+
var SUB_SYMBOL = new TexToken(6 /* CONTROL */, "_");
|
|
1443
|
+
var SUP_SYMBOL = new TexToken(6 /* CONTROL */, "^");
|
|
1378
1444
|
|
|
1379
1445
|
class LatexParser {
|
|
1380
1446
|
space_sensitive;
|
|
@@ -1392,11 +1458,13 @@ class LatexParser {
|
|
|
1392
1458
|
while (pos2 < tokens.length) {
|
|
1393
1459
|
const [res, newPos] = this.parseNextExpr(tokens, pos2);
|
|
1394
1460
|
pos2 = newPos;
|
|
1395
|
-
if (
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
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
|
+
}
|
|
1400
1468
|
}
|
|
1401
1469
|
if (res.type === "control" && res.content === "&") {
|
|
1402
1470
|
throw new LatexParserError("Unexpected & outside of an alignment");
|
|
@@ -1408,7 +1476,7 @@ class LatexParser {
|
|
|
1408
1476
|
} else if (results2.length === 1) {
|
|
1409
1477
|
return results2[0];
|
|
1410
1478
|
} else {
|
|
1411
|
-
return
|
|
1479
|
+
return new TexNode("ordgroup", "", results2);
|
|
1412
1480
|
}
|
|
1413
1481
|
}
|
|
1414
1482
|
if (results.length === 0) {
|
|
@@ -1416,7 +1484,7 @@ class LatexParser {
|
|
|
1416
1484
|
} else if (results.length === 1) {
|
|
1417
1485
|
return results[0];
|
|
1418
1486
|
} else {
|
|
1419
|
-
return
|
|
1487
|
+
return new TexNode("ordgroup", "", results);
|
|
1420
1488
|
}
|
|
1421
1489
|
}
|
|
1422
1490
|
parseNextExpr(tokens, start) {
|
|
@@ -1454,9 +1522,9 @@ class LatexParser {
|
|
|
1454
1522
|
res.sub = sub;
|
|
1455
1523
|
}
|
|
1456
1524
|
if (num_prime > 0) {
|
|
1457
|
-
res.sup =
|
|
1525
|
+
res.sup = new TexNode("ordgroup", "", []);
|
|
1458
1526
|
for (let i = 0;i < num_prime; i++) {
|
|
1459
|
-
res.sup.args.push(
|
|
1527
|
+
res.sup.args.push(new TexNode("element", "'"));
|
|
1460
1528
|
}
|
|
1461
1529
|
if (sup) {
|
|
1462
1530
|
res.sup.args.push(sup);
|
|
@@ -1467,7 +1535,7 @@ class LatexParser {
|
|
|
1467
1535
|
} else if (sup) {
|
|
1468
1536
|
res.sup = sup;
|
|
1469
1537
|
}
|
|
1470
|
-
return [
|
|
1538
|
+
return [new TexNode("supsub", "", [], res), pos];
|
|
1471
1539
|
} else {
|
|
1472
1540
|
return [base, pos];
|
|
1473
1541
|
}
|
|
@@ -1477,15 +1545,14 @@ class LatexParser {
|
|
|
1477
1545
|
const tokenType = firstToken.type;
|
|
1478
1546
|
switch (tokenType) {
|
|
1479
1547
|
case 0 /* ELEMENT */:
|
|
1480
|
-
return [
|
|
1548
|
+
return [new TexNode("element", firstToken.value), start + 1];
|
|
1481
1549
|
case 2 /* TEXT */:
|
|
1482
|
-
return [
|
|
1550
|
+
return [new TexNode("text", firstToken.value), start + 1];
|
|
1483
1551
|
case 3 /* COMMENT */:
|
|
1484
|
-
return [
|
|
1485
|
-
case 4 /*
|
|
1486
|
-
return [{ type: "whitespace", content: firstToken.value }, start + 1];
|
|
1552
|
+
return [new TexNode("comment", firstToken.value), start + 1];
|
|
1553
|
+
case 4 /* SPACE */:
|
|
1487
1554
|
case 5 /* NEWLINE */:
|
|
1488
|
-
return [
|
|
1555
|
+
return [new TexNode("whitespace", firstToken.value), start + 1];
|
|
1489
1556
|
case 1 /* COMMAND */:
|
|
1490
1557
|
if (firstToken.eq(BEGIN_COMMAND)) {
|
|
1491
1558
|
return this.parseBeginEndExpr(tokens, start);
|
|
@@ -1504,9 +1571,9 @@ class LatexParser {
|
|
|
1504
1571
|
case "}":
|
|
1505
1572
|
throw new LatexParserError("Unmatched '}'");
|
|
1506
1573
|
case "\\\\":
|
|
1507
|
-
return [
|
|
1574
|
+
return [new TexNode("control", "\\\\"), start + 1];
|
|
1508
1575
|
case "\\,":
|
|
1509
|
-
return [
|
|
1576
|
+
return [new TexNode("control", "\\,"), start + 1];
|
|
1510
1577
|
case "_": {
|
|
1511
1578
|
return [EMPTY_NODE, start];
|
|
1512
1579
|
}
|
|
@@ -1514,7 +1581,7 @@ class LatexParser {
|
|
|
1514
1581
|
return [EMPTY_NODE, start];
|
|
1515
1582
|
}
|
|
1516
1583
|
case "&":
|
|
1517
|
-
return [
|
|
1584
|
+
return [new TexNode("control", "&"), start + 1];
|
|
1518
1585
|
default:
|
|
1519
1586
|
throw new LatexParserError("Unknown control sequence");
|
|
1520
1587
|
}
|
|
@@ -1533,9 +1600,9 @@ class LatexParser {
|
|
|
1533
1600
|
switch (paramNum) {
|
|
1534
1601
|
case 0:
|
|
1535
1602
|
if (!symbolMap.has(command.slice(1))) {
|
|
1536
|
-
return [
|
|
1603
|
+
return [new TexNode("unknownMacro", command), pos];
|
|
1537
1604
|
}
|
|
1538
|
-
return [
|
|
1605
|
+
return [new TexNode("symbol", command), pos];
|
|
1539
1606
|
case 1: {
|
|
1540
1607
|
if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
|
|
1541
1608
|
const posLeftSquareBracket = pos;
|
|
@@ -1543,7 +1610,7 @@ class LatexParser {
|
|
|
1543
1610
|
const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
|
|
1544
1611
|
const exponent = this.parse(exprInside);
|
|
1545
1612
|
const [arg12, newPos2] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
|
|
1546
|
-
return [
|
|
1613
|
+
return [new TexNode("unaryFunc", command, [arg12], exponent), newPos2];
|
|
1547
1614
|
} else if (command === "\\text") {
|
|
1548
1615
|
if (pos + 2 >= tokens.length) {
|
|
1549
1616
|
throw new LatexParserError("Expecting content for \\text command");
|
|
@@ -1552,15 +1619,15 @@ class LatexParser {
|
|
|
1552
1619
|
assert(tokens[pos + 1].type === 2 /* TEXT */);
|
|
1553
1620
|
assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
|
|
1554
1621
|
const text = tokens[pos + 1].value;
|
|
1555
|
-
return [
|
|
1622
|
+
return [new TexNode("text", text), pos + 3];
|
|
1556
1623
|
}
|
|
1557
1624
|
let [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, pos);
|
|
1558
|
-
return [
|
|
1625
|
+
return [new TexNode("unaryFunc", command, [arg1]), newPos];
|
|
1559
1626
|
}
|
|
1560
1627
|
case 2: {
|
|
1561
1628
|
const [arg1, pos1] = this.parseNextExprWithoutSupSub(tokens, pos);
|
|
1562
1629
|
const [arg2, pos2] = this.parseNextExprWithoutSupSub(tokens, pos1);
|
|
1563
|
-
return [
|
|
1630
|
+
return [new TexNode("binaryFunc", command, [arg1, arg2]), pos2];
|
|
1564
1631
|
}
|
|
1565
1632
|
default:
|
|
1566
1633
|
throw new Error("Invalid number of parameters");
|
|
@@ -1597,11 +1664,11 @@ class LatexParser {
|
|
|
1597
1664
|
const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
|
|
1598
1665
|
const body = this.parse(exprInside);
|
|
1599
1666
|
const args = [
|
|
1600
|
-
|
|
1667
|
+
new TexNode("element", leftDelimiter.value),
|
|
1601
1668
|
body,
|
|
1602
|
-
|
|
1669
|
+
new TexNode("element", rightDelimiter.value)
|
|
1603
1670
|
];
|
|
1604
|
-
const res =
|
|
1671
|
+
const res = new TexNode("leftright", "", args);
|
|
1605
1672
|
return [res, pos];
|
|
1606
1673
|
}
|
|
1607
1674
|
parseBeginEndExpr(tokens, start) {
|
|
@@ -1628,11 +1695,11 @@ class LatexParser {
|
|
|
1628
1695
|
}
|
|
1629
1696
|
pos += 3;
|
|
1630
1697
|
const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
|
|
1631
|
-
while (exprInside.length > 0 && [4 /*
|
|
1698
|
+
while (exprInside.length > 0 && [4 /* SPACE */, 5 /* NEWLINE */].includes(exprInside[exprInside.length - 1].type)) {
|
|
1632
1699
|
exprInside.pop();
|
|
1633
1700
|
}
|
|
1634
1701
|
const body = this.parseAligned(exprInside);
|
|
1635
|
-
const res =
|
|
1702
|
+
const res = new TexNode("beginend", envName, [], body);
|
|
1636
1703
|
return [res, pos];
|
|
1637
1704
|
}
|
|
1638
1705
|
parseAligned(tokens) {
|
|
@@ -1640,22 +1707,26 @@ class LatexParser {
|
|
|
1640
1707
|
const allRows = [];
|
|
1641
1708
|
let row = [];
|
|
1642
1709
|
allRows.push(row);
|
|
1643
|
-
let group =
|
|
1710
|
+
let group = new TexNode("ordgroup", "", []);
|
|
1644
1711
|
row.push(group);
|
|
1645
1712
|
while (pos < tokens.length) {
|
|
1646
1713
|
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
1647
1714
|
pos = newPos;
|
|
1648
1715
|
if (res.type === "whitespace") {
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
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 === "\\\\") {
|
|
1653
1724
|
row = [];
|
|
1654
|
-
group =
|
|
1725
|
+
group = new TexNode("ordgroup", "", []);
|
|
1655
1726
|
row.push(group);
|
|
1656
1727
|
allRows.push(row);
|
|
1657
1728
|
} else if (res.type === "control" && res.content === "&") {
|
|
1658
|
-
group =
|
|
1729
|
+
group = new TexNode("ordgroup", "", []);
|
|
1659
1730
|
row.push(group);
|
|
1660
1731
|
} else {
|
|
1661
1732
|
group.args.push(res);
|
|
@@ -1669,87 +1740,62 @@ class LatexParser {
|
|
|
1669
1740
|
function is_delimiter(c) {
|
|
1670
1741
|
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
|
|
1671
1742
|
}
|
|
1672
|
-
function text_node_shallow_eq(a, b) {
|
|
1673
|
-
return a.type === b.type && a.content === b.content;
|
|
1674
|
-
}
|
|
1675
1743
|
function convert_overset(node) {
|
|
1676
1744
|
const [sup, base] = node.args;
|
|
1677
1745
|
const is_def = (n) => {
|
|
1678
|
-
if (n.
|
|
1746
|
+
if (n.eq_shallow(new TexNode("text", "def"))) {
|
|
1679
1747
|
return true;
|
|
1680
1748
|
}
|
|
1681
1749
|
if (n.type === "ordgroup" && n.args.length === 3) {
|
|
1682
1750
|
const [a1, a2, a3] = n.args;
|
|
1683
|
-
const d =
|
|
1684
|
-
const e =
|
|
1685
|
-
const f =
|
|
1686
|
-
if (
|
|
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)) {
|
|
1687
1755
|
return true;
|
|
1688
1756
|
}
|
|
1689
1757
|
}
|
|
1690
1758
|
return false;
|
|
1691
1759
|
};
|
|
1692
|
-
const is_eq = (n) => n.
|
|
1760
|
+
const is_eq = (n) => n.eq_shallow(new TexNode("element", "="));
|
|
1693
1761
|
if (is_def(sup) && is_eq(base)) {
|
|
1694
|
-
return
|
|
1695
|
-
type: "symbol",
|
|
1696
|
-
content: "eq.def"
|
|
1697
|
-
};
|
|
1762
|
+
return new TypstNode("symbol", "eq.def");
|
|
1698
1763
|
}
|
|
1699
|
-
const op_call =
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
};
|
|
1705
|
-
return {
|
|
1706
|
-
type: "supsub",
|
|
1707
|
-
content: "",
|
|
1708
|
-
data: {
|
|
1709
|
-
base: op_call,
|
|
1710
|
-
sup: convertTree(sup)
|
|
1711
|
-
}
|
|
1712
|
-
};
|
|
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
|
+
});
|
|
1713
1770
|
}
|
|
1714
1771
|
function convertTree(node) {
|
|
1715
1772
|
switch (node.type) {
|
|
1716
1773
|
case "empty":
|
|
1774
|
+
return new TypstNode("empty", "");
|
|
1717
1775
|
case "whitespace":
|
|
1718
|
-
return
|
|
1776
|
+
return new TypstNode("whitespace", node.content);
|
|
1719
1777
|
case "ordgroup":
|
|
1720
|
-
return
|
|
1721
|
-
type: "group",
|
|
1722
|
-
content: "",
|
|
1723
|
-
args: node.args.map(convertTree)
|
|
1724
|
-
};
|
|
1778
|
+
return new TypstNode("group", "", node.args.map(convertTree));
|
|
1725
1779
|
case "element":
|
|
1726
|
-
return
|
|
1780
|
+
return new TypstNode("atom", convertToken(node.content));
|
|
1727
1781
|
case "symbol":
|
|
1728
|
-
return
|
|
1782
|
+
return new TypstNode("symbol", convertToken(node.content));
|
|
1729
1783
|
case "text":
|
|
1730
|
-
return
|
|
1784
|
+
return new TypstNode("text", node.content);
|
|
1731
1785
|
case "comment":
|
|
1732
|
-
return
|
|
1786
|
+
return new TypstNode("comment", node.content);
|
|
1733
1787
|
case "supsub": {
|
|
1734
1788
|
let { base, sup, sub } = node.data;
|
|
1735
1789
|
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
1736
|
-
return
|
|
1737
|
-
type: "binaryFunc",
|
|
1738
|
-
content: "overbrace",
|
|
1739
|
-
args: [convertTree(base.args[0]), convertTree(sup)]
|
|
1740
|
-
};
|
|
1790
|
+
return new TypstNode("binaryFunc", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
|
|
1741
1791
|
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
1742
|
-
return
|
|
1743
|
-
type: "binaryFunc",
|
|
1744
|
-
content: "underbrace",
|
|
1745
|
-
args: [convertTree(base.args[0]), convertTree(sub)]
|
|
1746
|
-
};
|
|
1792
|
+
return new TypstNode("binaryFunc", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
|
|
1747
1793
|
}
|
|
1748
1794
|
const data = {
|
|
1749
1795
|
base: convertTree(base)
|
|
1750
1796
|
};
|
|
1751
1797
|
if (data.base.type === "empty") {
|
|
1752
|
-
data.base =
|
|
1798
|
+
data.base = new TypstNode("text", "");
|
|
1753
1799
|
}
|
|
1754
1800
|
if (sup) {
|
|
1755
1801
|
data.sup = convertTree(sup);
|
|
@@ -1757,19 +1803,11 @@ function convertTree(node) {
|
|
|
1757
1803
|
if (sub) {
|
|
1758
1804
|
data.sub = convertTree(sub);
|
|
1759
1805
|
}
|
|
1760
|
-
return
|
|
1761
|
-
type: "supsub",
|
|
1762
|
-
content: "",
|
|
1763
|
-
data
|
|
1764
|
-
};
|
|
1806
|
+
return new TypstNode("supsub", "", [], data);
|
|
1765
1807
|
}
|
|
1766
1808
|
case "leftright": {
|
|
1767
1809
|
const [left, body, right] = node.args;
|
|
1768
|
-
const group =
|
|
1769
|
-
type: "group",
|
|
1770
|
-
content: "",
|
|
1771
|
-
args: node.args.map(convertTree)
|
|
1772
|
-
};
|
|
1810
|
+
const group = new TypstNode("group", "", node.args.map(convertTree));
|
|
1773
1811
|
if ([
|
|
1774
1812
|
"[]",
|
|
1775
1813
|
"()",
|
|
@@ -1780,49 +1818,26 @@ function convertTree(node) {
|
|
|
1780
1818
|
].includes(left.content + right.content)) {
|
|
1781
1819
|
return group;
|
|
1782
1820
|
}
|
|
1783
|
-
return
|
|
1784
|
-
type: "unaryFunc",
|
|
1785
|
-
content: "lr",
|
|
1786
|
-
args: [group]
|
|
1787
|
-
};
|
|
1821
|
+
return new TypstNode("unaryFunc", "lr", [group]);
|
|
1788
1822
|
}
|
|
1789
1823
|
case "binaryFunc": {
|
|
1790
1824
|
if (node.content === "\\overset") {
|
|
1791
1825
|
return convert_overset(node);
|
|
1792
1826
|
}
|
|
1793
|
-
return
|
|
1794
|
-
type: "binaryFunc",
|
|
1795
|
-
content: convertToken(node.content),
|
|
1796
|
-
args: node.args.map(convertTree)
|
|
1797
|
-
};
|
|
1827
|
+
return new TypstNode("binaryFunc", convertToken(node.content), node.args.map(convertTree));
|
|
1798
1828
|
}
|
|
1799
1829
|
case "unaryFunc": {
|
|
1800
1830
|
const arg0 = convertTree(node.args[0]);
|
|
1801
1831
|
if (node.content === "\\sqrt" && node.data) {
|
|
1802
1832
|
const data = convertTree(node.data);
|
|
1803
|
-
return
|
|
1804
|
-
type: "binaryFunc",
|
|
1805
|
-
content: "root",
|
|
1806
|
-
args: [data, arg0]
|
|
1807
|
-
};
|
|
1833
|
+
return new TypstNode("binaryFunc", "root", [data, arg0]);
|
|
1808
1834
|
}
|
|
1809
1835
|
if (node.content === "\\mathbf") {
|
|
1810
|
-
const inner =
|
|
1811
|
-
|
|
1812
|
-
content: "bold",
|
|
1813
|
-
args: [arg0]
|
|
1814
|
-
};
|
|
1815
|
-
return {
|
|
1816
|
-
type: "unaryFunc",
|
|
1817
|
-
content: "upright",
|
|
1818
|
-
args: [inner]
|
|
1819
|
-
};
|
|
1836
|
+
const inner = new TypstNode("unaryFunc", "bold", [arg0]);
|
|
1837
|
+
return new TypstNode("unaryFunc", "upright", [inner]);
|
|
1820
1838
|
}
|
|
1821
1839
|
if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
|
|
1822
|
-
return
|
|
1823
|
-
type: "symbol",
|
|
1824
|
-
content: arg0.content + arg0.content
|
|
1825
|
-
};
|
|
1840
|
+
return new TypstNode("symbol", arg0.content + arg0.content);
|
|
1826
1841
|
}
|
|
1827
1842
|
if (node.content === "\\operatorname") {
|
|
1828
1843
|
const body = node.args;
|
|
@@ -1831,50 +1846,31 @@ function convertTree(node) {
|
|
|
1831
1846
|
}
|
|
1832
1847
|
const text = body[0].content;
|
|
1833
1848
|
if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
|
|
1834
|
-
return
|
|
1835
|
-
type: "symbol",
|
|
1836
|
-
content: text
|
|
1837
|
-
};
|
|
1849
|
+
return new TypstNode("symbol", text);
|
|
1838
1850
|
} else {
|
|
1839
|
-
return
|
|
1840
|
-
type: "unaryFunc",
|
|
1841
|
-
content: "op",
|
|
1842
|
-
args: [{ type: "text", content: text }]
|
|
1843
|
-
};
|
|
1851
|
+
return new TypstNode("unaryFunc", "op", [new TypstNode("text", text)]);
|
|
1844
1852
|
}
|
|
1845
1853
|
}
|
|
1846
|
-
return
|
|
1847
|
-
type: "unaryFunc",
|
|
1848
|
-
content: convertToken(node.content),
|
|
1849
|
-
args: node.args.map(convertTree)
|
|
1850
|
-
};
|
|
1854
|
+
return new TypstNode("unaryFunc", convertToken(node.content), node.args.map(convertTree));
|
|
1851
1855
|
}
|
|
1852
|
-
case "newline":
|
|
1853
|
-
return { type: "newline", content: "\n" };
|
|
1854
1856
|
case "beginend": {
|
|
1855
1857
|
const matrix = node.data;
|
|
1856
1858
|
const data = matrix.map((row) => row.map(convertTree));
|
|
1857
1859
|
if (node.content.startsWith("align")) {
|
|
1858
|
-
return
|
|
1859
|
-
type: "align",
|
|
1860
|
-
content: "",
|
|
1861
|
-
data
|
|
1862
|
-
};
|
|
1860
|
+
return new TypstNode("align", "", [], data);
|
|
1863
1861
|
} else {
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
data
|
|
1868
|
-
};
|
|
1862
|
+
const res = new TypstNode("matrix", "", [], data);
|
|
1863
|
+
res.setOptions({ delim: "#none" });
|
|
1864
|
+
return res;
|
|
1869
1865
|
}
|
|
1870
1866
|
}
|
|
1871
1867
|
case "unknownMacro":
|
|
1872
|
-
return
|
|
1868
|
+
return new TypstNode("unknown", convertToken(node.content));
|
|
1873
1869
|
case "control":
|
|
1874
1870
|
if (node.content === "\\\\") {
|
|
1875
|
-
return
|
|
1871
|
+
return new TypstNode("symbol", "\\");
|
|
1876
1872
|
} else if (node.content === "\\,") {
|
|
1877
|
-
return
|
|
1873
|
+
return new TypstNode("symbol", "thin");
|
|
1878
1874
|
} else {
|
|
1879
1875
|
throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
|
|
1880
1876
|
}
|
|
@@ -1889,6 +1885,8 @@ function convertToken(token) {
|
|
|
1889
1885
|
return "\\/";
|
|
1890
1886
|
} else if (token === "\\|") {
|
|
1891
1887
|
return "parallel";
|
|
1888
|
+
} else if (token === "\\colon") {
|
|
1889
|
+
return ":";
|
|
1892
1890
|
} else if (token === "\\\\") {
|
|
1893
1891
|
return "\\";
|
|
1894
1892
|
} else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
|
|
@@ -1912,6 +1910,10 @@ var TYPST_INTRINSIC_SYMBOLS = [
|
|
|
1912
1910
|
"sech",
|
|
1913
1911
|
"csch"
|
|
1914
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");
|
|
1915
1917
|
|
|
1916
1918
|
class TypstWriterError extends Error {
|
|
1917
1919
|
node;
|
|
@@ -1925,57 +1927,72 @@ class TypstWriterError extends Error {
|
|
|
1925
1927
|
class TypstWriter {
|
|
1926
1928
|
nonStrict;
|
|
1927
1929
|
preferTypstIntrinsic;
|
|
1930
|
+
keepSpaces;
|
|
1928
1931
|
buffer = "";
|
|
1929
1932
|
queue = [];
|
|
1930
|
-
needSpaceAfterSingleItemScript = false;
|
|
1931
1933
|
insideFunctionDepth = 0;
|
|
1932
|
-
constructor(nonStrict, preferTypstIntrinsic) {
|
|
1934
|
+
constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
|
|
1933
1935
|
this.nonStrict = nonStrict;
|
|
1934
1936
|
this.preferTypstIntrinsic = preferTypstIntrinsic;
|
|
1937
|
+
this.keepSpaces = keepSpaces;
|
|
1935
1938
|
}
|
|
1936
|
-
writeBuffer(
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
let no_need_space = false;
|
|
1941
|
-
no_need_space ||= /[\(\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
1942
|
-
no_need_space ||= /^[}()_^,;!\|]$/.test(str);
|
|
1943
|
-
no_need_space ||= str === "'";
|
|
1944
|
-
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1945
|
-
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
1946
|
-
no_need_space ||= str.startsWith("\n");
|
|
1947
|
-
no_need_space ||= this.buffer === "";
|
|
1948
|
-
no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
|
|
1949
|
-
if (!no_need_space) {
|
|
1950
|
-
this.buffer += " ";
|
|
1951
|
-
}
|
|
1939
|
+
writeBuffer(token) {
|
|
1940
|
+
const str = token.content;
|
|
1941
|
+
if (str === "") {
|
|
1942
|
+
return;
|
|
1952
1943
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
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 += " ";
|
|
1955
1956
|
}
|
|
1956
1957
|
this.buffer += str;
|
|
1957
1958
|
}
|
|
1958
|
-
|
|
1959
|
+
serialize(node) {
|
|
1959
1960
|
switch (node.type) {
|
|
1960
1961
|
case "empty":
|
|
1961
1962
|
break;
|
|
1962
1963
|
case "atom": {
|
|
1963
1964
|
if (node.content === "," && this.insideFunctionDepth > 0) {
|
|
1964
|
-
this.queue.push(
|
|
1965
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "comma"));
|
|
1965
1966
|
} else {
|
|
1966
|
-
this.queue.push(
|
|
1967
|
+
this.queue.push(new TypstToken(1 /* ATOM */, node.content));
|
|
1967
1968
|
}
|
|
1968
1969
|
break;
|
|
1969
1970
|
}
|
|
1970
1971
|
case "symbol":
|
|
1972
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
|
|
1973
|
+
break;
|
|
1971
1974
|
case "text":
|
|
1975
|
+
this.queue.push(new TypstToken(2 /* TEXT */, `"${node.content}"`));
|
|
1976
|
+
break;
|
|
1972
1977
|
case "comment":
|
|
1973
|
-
|
|
1974
|
-
|
|
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
|
+
}
|
|
1975
1992
|
break;
|
|
1976
1993
|
case "group":
|
|
1977
1994
|
for (const item of node.args) {
|
|
1978
|
-
this.
|
|
1995
|
+
this.serialize(item);
|
|
1979
1996
|
}
|
|
1980
1997
|
break;
|
|
1981
1998
|
case "supsub": {
|
|
@@ -1984,48 +2001,48 @@ class TypstWriter {
|
|
|
1984
2001
|
let trailing_space_needed = false;
|
|
1985
2002
|
const has_prime = sup && sup.type === "atom" && sup.content === "\'";
|
|
1986
2003
|
if (has_prime) {
|
|
1987
|
-
this.queue.push(
|
|
2004
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "\'"));
|
|
1988
2005
|
trailing_space_needed = false;
|
|
1989
2006
|
}
|
|
1990
2007
|
if (sub) {
|
|
1991
|
-
this.queue.push(
|
|
2008
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "_"));
|
|
1992
2009
|
trailing_space_needed = this.appendWithBracketsIfNeeded(sub);
|
|
1993
2010
|
}
|
|
1994
2011
|
if (sup && !has_prime) {
|
|
1995
|
-
this.queue.push(
|
|
2012
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "^"));
|
|
1996
2013
|
trailing_space_needed = this.appendWithBracketsIfNeeded(sup);
|
|
1997
2014
|
}
|
|
1998
2015
|
if (trailing_space_needed) {
|
|
1999
|
-
this.queue.push(
|
|
2016
|
+
this.queue.push(new TypstToken(6 /* CONTROL */, " "));
|
|
2000
2017
|
}
|
|
2001
2018
|
break;
|
|
2002
2019
|
}
|
|
2003
2020
|
case "binaryFunc": {
|
|
2004
|
-
const func_symbol =
|
|
2021
|
+
const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
|
|
2005
2022
|
const [arg0, arg1] = node.args;
|
|
2006
2023
|
this.queue.push(func_symbol);
|
|
2007
2024
|
this.insideFunctionDepth++;
|
|
2008
|
-
this.queue.push(
|
|
2009
|
-
this.
|
|
2010
|
-
this.queue.push(
|
|
2011
|
-
this.
|
|
2012
|
-
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);
|
|
2013
2030
|
this.insideFunctionDepth--;
|
|
2014
2031
|
break;
|
|
2015
2032
|
}
|
|
2016
2033
|
case "unaryFunc": {
|
|
2017
|
-
const func_symbol =
|
|
2034
|
+
const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
|
|
2018
2035
|
const arg0 = node.args[0];
|
|
2019
2036
|
this.queue.push(func_symbol);
|
|
2020
2037
|
this.insideFunctionDepth++;
|
|
2021
|
-
this.queue.push(
|
|
2022
|
-
this.
|
|
2038
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2039
|
+
this.serialize(arg0);
|
|
2023
2040
|
if (node.options) {
|
|
2024
2041
|
for (const [key, value] of Object.entries(node.options)) {
|
|
2025
|
-
this.queue.push(
|
|
2042
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, `, ${key}: ${value}`));
|
|
2026
2043
|
}
|
|
2027
2044
|
}
|
|
2028
|
-
this.queue.push(
|
|
2045
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2029
2046
|
this.insideFunctionDepth--;
|
|
2030
2047
|
break;
|
|
2031
2048
|
}
|
|
@@ -2034,41 +2051,45 @@ class TypstWriter {
|
|
|
2034
2051
|
matrix.forEach((row, i) => {
|
|
2035
2052
|
row.forEach((cell, j) => {
|
|
2036
2053
|
if (j > 0) {
|
|
2037
|
-
this.queue.push(
|
|
2054
|
+
this.queue.push(new TypstToken(1 /* ATOM */, "&"));
|
|
2038
2055
|
}
|
|
2039
|
-
this.
|
|
2056
|
+
this.serialize(cell);
|
|
2040
2057
|
});
|
|
2041
2058
|
if (i < matrix.length - 1) {
|
|
2042
|
-
this.queue.push(
|
|
2059
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "\\"));
|
|
2043
2060
|
}
|
|
2044
2061
|
});
|
|
2045
2062
|
break;
|
|
2046
2063
|
}
|
|
2047
2064
|
case "matrix": {
|
|
2048
2065
|
const matrix = node.data;
|
|
2049
|
-
this.queue.push(
|
|
2066
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, "mat"));
|
|
2050
2067
|
this.insideFunctionDepth++;
|
|
2051
|
-
this.queue.push(
|
|
2052
|
-
|
|
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
|
+
}
|
|
2053
2074
|
matrix.forEach((row, i) => {
|
|
2054
2075
|
row.forEach((cell, j) => {
|
|
2055
|
-
this.
|
|
2076
|
+
this.serialize(cell);
|
|
2056
2077
|
if (j < row.length - 1) {
|
|
2057
|
-
this.queue.push(
|
|
2078
|
+
this.queue.push(new TypstToken(1 /* ATOM */, ","));
|
|
2058
2079
|
} else {
|
|
2059
2080
|
if (i < matrix.length - 1) {
|
|
2060
|
-
this.queue.push(
|
|
2081
|
+
this.queue.push(new TypstToken(1 /* ATOM */, ";"));
|
|
2061
2082
|
}
|
|
2062
2083
|
}
|
|
2063
2084
|
});
|
|
2064
2085
|
});
|
|
2065
|
-
this.queue.push(
|
|
2086
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2066
2087
|
this.insideFunctionDepth--;
|
|
2067
2088
|
break;
|
|
2068
2089
|
}
|
|
2069
2090
|
case "unknown": {
|
|
2070
2091
|
if (this.nonStrict) {
|
|
2071
|
-
this.queue.push(
|
|
2092
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
|
|
2072
2093
|
} else {
|
|
2073
2094
|
throw new TypstWriterError(`Unknown macro: ${node.content}`, node);
|
|
2074
2095
|
}
|
|
@@ -2088,41 +2109,28 @@ class TypstWriter {
|
|
|
2088
2109
|
}
|
|
2089
2110
|
}
|
|
2090
2111
|
if (need_to_wrap) {
|
|
2091
|
-
this.queue.push(
|
|
2092
|
-
this.
|
|
2093
|
-
this.queue.push(
|
|
2112
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2113
|
+
this.serialize(node);
|
|
2114
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2094
2115
|
} else {
|
|
2095
|
-
this.
|
|
2116
|
+
this.serialize(node);
|
|
2096
2117
|
}
|
|
2097
2118
|
return !need_to_wrap;
|
|
2098
2119
|
}
|
|
2099
2120
|
flushQueue() {
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
break;
|
|
2110
|
-
case "softSpace":
|
|
2111
|
-
this.needSpaceAfterSingleItemScript = true;
|
|
2112
|
-
str = "";
|
|
2113
|
-
break;
|
|
2114
|
-
case "comment":
|
|
2115
|
-
str = `//${node.content}`;
|
|
2116
|
-
break;
|
|
2117
|
-
case "newline":
|
|
2118
|
-
str = "\n";
|
|
2119
|
-
break;
|
|
2120
|
-
default:
|
|
2121
|
-
throw new TypstWriterError(`Unexpected node type to stringify: ${node.type}`, node);
|
|
2122
|
-
}
|
|
2123
|
-
if (str !== "") {
|
|
2124
|
-
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
|
+
}
|
|
2125
2130
|
}
|
|
2131
|
+
}
|
|
2132
|
+
this.queue.forEach((token) => {
|
|
2133
|
+
this.writeBuffer(token);
|
|
2126
2134
|
});
|
|
2127
2135
|
this.queue = [];
|
|
2128
2136
|
}
|
|
@@ -2156,6 +2164,7 @@ function tex2typst(tex, options) {
|
|
|
2156
2164
|
const opt = {
|
|
2157
2165
|
nonStrict: true,
|
|
2158
2166
|
preferTypstIntrinsic: true,
|
|
2167
|
+
keepSpaces: false,
|
|
2159
2168
|
customTexMacros: {}
|
|
2160
2169
|
};
|
|
2161
2170
|
if (options) {
|
|
@@ -2171,8 +2180,8 @@ function tex2typst(tex, options) {
|
|
|
2171
2180
|
}
|
|
2172
2181
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
2173
2182
|
const typstTree = convertTree(texTree);
|
|
2174
|
-
const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic);
|
|
2175
|
-
writer2.
|
|
2183
|
+
const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
|
|
2184
|
+
writer2.serialize(typstTree);
|
|
2176
2185
|
return writer2.finalize();
|
|
2177
2186
|
}
|
|
2178
2187
|
export {
|