tex2typst 0.2.16 → 0.3.0-beta-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,7 +7,6 @@ var symbolMap = new Map([
7
7
  ["ddot", "dot.double"],
8
8
  ["doteq", "dot(eq)"],
9
9
  ["dots", "dots.h"],
10
- ["ldots", "dots.h"],
11
10
  ["vdots", "dots.v"],
12
11
  ["ddots", "dots.down"],
13
12
  ["widehat", "hat"],
@@ -23,6 +22,7 @@ var symbolMap = new Map([
23
22
  ["tbinom", "binom"],
24
23
  ["dfrac", "frac"],
25
24
  ["tfrac", "frac"],
25
+ ["operatorname", "op"],
26
26
  ["boldsymbol", "bold"],
27
27
  ["mathbb", "bb"],
28
28
  ["mathbf", "bold"],
@@ -42,7 +42,7 @@ var symbolMap = new Map([
42
42
  ["approx", "approx"],
43
43
  ["cong", "tilde.equiv"],
44
44
  ["simeq", "tilde.eq"],
45
- ["asymp", "\u224D"],
45
+ ["asymp", ""],
46
46
  ["equiv", "equiv"],
47
47
  ["propto", "prop"],
48
48
  ["gets", "arrow.l"],
@@ -937,62 +937,6 @@ var map_from_official_docs = new Map([
937
937
  ["barV", "tack.b.double"],
938
938
  ["shortdowntack", "tack.b.short"],
939
939
  ["dashVdash", "tack.l.r"],
940
- ["mupalpha", "alpha"],
941
- ["mupbeta", "beta"],
942
- ["mupchi", "chi"],
943
- ["mupdelta", "delta"],
944
- ["mupvarepsilon", "epsilon"],
945
- ["mupepsilon", "epsilon.alt"],
946
- ["mupeta", "eta"],
947
- ["mupgamma", "gamma"],
948
- ["mupiota", "iota"],
949
- ["mupkappa", "kappa"],
950
- ["mupvarkappa", "kappa.alt"],
951
- ["muplambda", "lambda"],
952
- ["mupmu", "mu"],
953
- ["mupnu", "nu"],
954
- ["mho", "ohm.inv"],
955
- ["mupomega", "omega"],
956
- ["mupomicron", "omicron"],
957
- ["mupvarphi", "phi"],
958
- ["mupphi", "phi.alt"],
959
- ["muppi", "pi"],
960
- ["mupvarpi", "pi.alt"],
961
- ["muppsi", "psi"],
962
- ["muprho", "rho"],
963
- ["mupvarrho", "rho.alt"],
964
- ["mupsigma", "sigma"],
965
- ["mupvarsigma", "sigma.alt"],
966
- ["muptau", "tau"],
967
- ["muptheta", "theta"],
968
- ["mupvartheta", "theta.alt"],
969
- ["mupupsilon", "upsilon"],
970
- ["mupxi", "xi"],
971
- ["mupzeta", "zeta"],
972
- ["mupAlpha", "Alpha"],
973
- ["mupBeta", "Beta"],
974
- ["mupChi", "Chi"],
975
- ["mupDelta", "Delta"],
976
- ["mupEpsilon", "Epsilon"],
977
- ["mupEta", "Eta"],
978
- ["mupGamma", "Gamma"],
979
- ["mupIota", "Iota"],
980
- ["mupKappa", "Kappa"],
981
- ["mupLambda", "Lambda"],
982
- ["mupMu", "Mu"],
983
- ["mupNu", "Nu"],
984
- ["mupOmega", "Omega"],
985
- ["mupOmicron", "Omicron"],
986
- ["mupPhi", "Phi"],
987
- ["mupPi", "Pi"],
988
- ["mupPsi", "Psi"],
989
- ["mupRho", "Rho"],
990
- ["mupSigma", "Sigma"],
991
- ["mupTau", "Tau"],
992
- ["mupTheta", "Theta"],
993
- ["mupUpsilon", "Upsilon"],
994
- ["mupXi", "Xi"],
995
- ["mupZeta", "Zeta"],
996
940
  ["BbbA", "AA"],
997
941
  ["BbbB", "BB"],
998
942
  ["BbbC", "CC"],
@@ -1033,8 +977,84 @@ for (const [key, value] of map_from_official_docs) {
1033
977
  symbolMap.set(key, value);
1034
978
  }
1035
979
  }
980
+ var reverseSymbolMap = new Map;
981
+ for (const [key, value] of Array.from(symbolMap.entries()).reverse()) {
982
+ reverseSymbolMap.set(value, key);
983
+ }
984
+ reverseSymbolMap.set("dif", "mathrm{d}");
985
+ var typst_to_tex_map = new Map([
986
+ ["top", "top"],
987
+ ["frac", "frac"],
988
+ ["tilde", "tilde"],
989
+ ["hat", "hat"],
990
+ ["upright", "mathrm"],
991
+ ["bold", "boldsymbol"]
992
+ ]);
993
+ for (const [key, value] of typst_to_tex_map) {
994
+ reverseSymbolMap.set(key, value);
995
+ }
996
+
997
+ // src/generic.ts
998
+ function array_find(array, item, start = 0) {
999
+ for (let i = start;i < array.length; i++) {
1000
+ if (array[i].eq(item)) {
1001
+ return i;
1002
+ }
1003
+ }
1004
+ return -1;
1005
+ }
1006
+ function array_includes(array, item) {
1007
+ for (const i of array) {
1008
+ if (i.eq(item)) {
1009
+ return true;
1010
+ }
1011
+ }
1012
+ return false;
1013
+ }
1014
+ function array_split(array, sep) {
1015
+ const res = [];
1016
+ let current_slice = [];
1017
+ for (const i of array) {
1018
+ if (i.eq(sep)) {
1019
+ res.push(current_slice);
1020
+ current_slice = [];
1021
+ } else {
1022
+ current_slice.push(i);
1023
+ }
1024
+ }
1025
+ res.push(current_slice);
1026
+ return res;
1027
+ }
1036
1028
 
1037
1029
  // src/types.ts
1030
+ class TexToken {
1031
+ type;
1032
+ value;
1033
+ constructor(type, value) {
1034
+ this.type = type;
1035
+ this.value = value;
1036
+ }
1037
+ eq(token) {
1038
+ return this.type === token.type && this.value === token.value;
1039
+ }
1040
+ toString() {
1041
+ switch (this.type) {
1042
+ case 2 /* TEXT */:
1043
+ return `\\text{${this.value}}`;
1044
+ case 3 /* COMMENT */:
1045
+ return `%${this.value}`;
1046
+ default:
1047
+ return this.value;
1048
+ }
1049
+ }
1050
+ }
1051
+ function apply_escape_if_needed(c) {
1052
+ if (["{", "}", "%"].includes(c)) {
1053
+ return "\\" + c;
1054
+ }
1055
+ return c;
1056
+ }
1057
+
1038
1058
  class TexNode {
1039
1059
  type;
1040
1060
  content;
@@ -1046,7 +1066,7 @@ class TexNode {
1046
1066
  this.args = args;
1047
1067
  this.data = data;
1048
1068
  }
1049
- eq_shallow(other) {
1069
+ eq(other) {
1050
1070
  return this.type === other.type && this.content === other.content;
1051
1071
  }
1052
1072
  toString() {
@@ -1057,26 +1077,174 @@ class TexNode {
1057
1077
  throw new Error(`toString() is not implemented for type ${this.type}`);
1058
1078
  }
1059
1079
  }
1080
+ serialize() {
1081
+ switch (this.type) {
1082
+ case "empty":
1083
+ return [];
1084
+ case "element": {
1085
+ let c = this.content;
1086
+ c = apply_escape_if_needed(c);
1087
+ return [new TexToken(0 /* ELEMENT */, c)];
1088
+ }
1089
+ case "symbol":
1090
+ return [new TexToken(1 /* COMMAND */, this.content)];
1091
+ case "text":
1092
+ return [new TexToken(2 /* TEXT */, this.content)];
1093
+ case "comment":
1094
+ return [new TexToken(3 /* COMMENT */, this.content)];
1095
+ case "whitespace": {
1096
+ const tokens = [];
1097
+ for (const c of this.content) {
1098
+ const token_type = c === " " ? 4 /* SPACE */ : 5 /* NEWLINE */;
1099
+ tokens.push(new TexToken(token_type, c));
1100
+ }
1101
+ return tokens;
1102
+ }
1103
+ case "ordgroup":
1104
+ return this.args.map((n) => n.serialize()).flat();
1105
+ case "unaryFunc": {
1106
+ let tokens = [];
1107
+ tokens.push(new TexToken(1 /* COMMAND */, this.content));
1108
+ if (this.content === "\\sqrt" && this.data) {
1109
+ tokens.push(new TexToken(0 /* ELEMENT */, "["));
1110
+ tokens = tokens.concat(this.data.serialize());
1111
+ tokens.push(new TexToken(0 /* ELEMENT */, "]"));
1112
+ }
1113
+ if (this.content === "\\operatorname" && this.args.length === 1 && this.args[0].type === "text") {
1114
+ const text = this.args[0].content;
1115
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1116
+ tokens.push(new TexToken(1 /* COMMAND */, text));
1117
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1118
+ return tokens;
1119
+ }
1120
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1121
+ tokens = tokens.concat(this.args[0].serialize());
1122
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1123
+ return tokens;
1124
+ }
1125
+ case "binaryFunc": {
1126
+ let tokens = [];
1127
+ tokens.push(new TexToken(1 /* COMMAND */, this.content));
1128
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1129
+ tokens = tokens.concat(this.args[0].serialize());
1130
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1131
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1132
+ tokens = tokens.concat(this.args[1].serialize());
1133
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1134
+ return tokens;
1135
+ }
1136
+ case "supsub": {
1137
+ let tokens = [];
1138
+ const { base, sup, sub } = this.data;
1139
+ tokens = tokens.concat(base.serialize());
1140
+ if (sub) {
1141
+ tokens.push(new TexToken(6 /* CONTROL */, "_"));
1142
+ if (sub.type === "ordgroup" || sub.type === "supsub" || sub.type === "empty") {
1143
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1144
+ tokens = tokens.concat(sub.serialize());
1145
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1146
+ } else {
1147
+ tokens = tokens.concat(sub.serialize());
1148
+ }
1149
+ }
1150
+ if (sup) {
1151
+ tokens.push(new TexToken(6 /* CONTROL */, "^"));
1152
+ if (sup.type === "ordgroup" || sup.type === "supsub" || sup.type === "empty") {
1153
+ tokens.push(new TexToken(0 /* ELEMENT */, "{"));
1154
+ tokens = tokens.concat(sup.serialize());
1155
+ tokens.push(new TexToken(0 /* ELEMENT */, "}"));
1156
+ } else {
1157
+ tokens = tokens.concat(sup.serialize());
1158
+ }
1159
+ }
1160
+ return tokens;
1161
+ }
1162
+ case "control": {
1163
+ return [new TexToken(6 /* CONTROL */, this.content)];
1164
+ }
1165
+ case "beginend": {
1166
+ let tokens = [];
1167
+ const matrix = this.data;
1168
+ tokens.push(new TexToken(1 /* COMMAND */, `\\begin{${this.content}}`));
1169
+ tokens.push(new TexToken(5 /* NEWLINE */, `
1170
+ `));
1171
+ for (let i = 0;i < matrix.length; i++) {
1172
+ const row = matrix[i];
1173
+ for (let j = 0;j < row.length; j++) {
1174
+ const cell = row[j];
1175
+ tokens = tokens.concat(cell.serialize());
1176
+ if (j !== row.length - 1) {
1177
+ tokens.push(new TexToken(6 /* CONTROL */, "&"));
1178
+ }
1179
+ }
1180
+ if (i !== matrix.length - 1) {
1181
+ tokens.push(new TexToken(6 /* CONTROL */, "\\\\"));
1182
+ }
1183
+ }
1184
+ tokens.push(new TexToken(5 /* NEWLINE */, `
1185
+ `));
1186
+ tokens.push(new TexToken(1 /* COMMAND */, `\\end{${this.content}}`));
1187
+ return tokens;
1188
+ }
1189
+ default:
1190
+ throw new Error("[TexNode.serialize] Unimplemented type: " + this.type);
1191
+ }
1192
+ }
1060
1193
  }
1061
1194
  class TypstToken {
1062
1195
  type;
1063
- content;
1196
+ value;
1064
1197
  constructor(type, content) {
1065
1198
  this.type = type;
1066
- this.content = content;
1199
+ this.value = content;
1067
1200
  }
1068
1201
  eq(other) {
1069
- return this.type === other.type && this.content === other.content;
1202
+ return this.type === other.type && this.value === other.value;
1070
1203
  }
1071
1204
  isOneOf(tokens) {
1072
- let found = false;
1073
- for (const token of tokens) {
1074
- if (this.eq(token)) {
1075
- found = true;
1076
- break;
1205
+ return array_includes(tokens, this);
1206
+ }
1207
+ toNode() {
1208
+ switch (this.type) {
1209
+ case 2 /* TEXT */:
1210
+ return new TypstNode("text", this.value);
1211
+ case 3 /* COMMENT */:
1212
+ return new TypstNode("comment", this.value);
1213
+ case 4 /* SPACE */:
1214
+ case 7 /* NEWLINE */:
1215
+ return new TypstNode("whitespace", this.value);
1216
+ case 1 /* ELEMENT */:
1217
+ return new TypstNode("atom", this.value);
1218
+ case 0 /* SYMBOL */:
1219
+ return new TypstNode("symbol", this.value);
1220
+ case 6 /* CONTROL */: {
1221
+ const controlChar = this.value;
1222
+ switch (controlChar) {
1223
+ case "":
1224
+ case "_":
1225
+ case "^":
1226
+ return new TypstNode("empty", "");
1227
+ case "&":
1228
+ return new TypstNode("control", "&");
1229
+ case "\\":
1230
+ return new TypstNode("control", "\\");
1231
+ default:
1232
+ throw new Error(`Unexpected control character ${controlChar}`);
1233
+ }
1077
1234
  }
1235
+ default:
1236
+ throw new Error(`Unexpected token type ${this.type}`);
1237
+ }
1238
+ }
1239
+ toString() {
1240
+ switch (this.type) {
1241
+ case 2 /* TEXT */:
1242
+ return `"${this.value}"`;
1243
+ case 3 /* COMMENT */:
1244
+ return `//${this.value}`;
1245
+ default:
1246
+ return this.value;
1078
1247
  }
1079
- return found;
1080
1248
  }
1081
1249
  }
1082
1250
 
@@ -1095,17 +1263,65 @@ class TypstNode {
1095
1263
  setOptions(options) {
1096
1264
  this.options = options;
1097
1265
  }
1098
- eq_shallow(other) {
1266
+ eq(other) {
1099
1267
  return this.type === other.type && this.content === other.content;
1100
1268
  }
1101
1269
  }
1102
1270
 
1103
- // src/tex-parser.ts
1271
+ // src/util.ts
1272
+ function isalpha(char) {
1273
+ return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(char);
1274
+ }
1275
+ function isdigit(char) {
1276
+ return "0123456789".includes(char);
1277
+ }
1104
1278
  function assert(condition, message = "") {
1105
1279
  if (!condition) {
1106
- throw new LatexParserError(message);
1280
+ throw new Error(message);
1107
1281
  }
1108
1282
  }
1283
+
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", "");
1109
1325
  function get_command_param_num(command) {
1110
1326
  if (UNARY_COMMANDS.includes(command)) {
1111
1327
  return 1;
@@ -1115,46 +1331,10 @@ function get_command_param_num(command) {
1115
1331
  return 0;
1116
1332
  }
1117
1333
  }
1118
- function find_closing_curly_bracket(tokens, start) {
1119
- assert(tokens[start].eq(LEFT_CURLY_BRACKET));
1120
- let count = 1;
1121
- let pos = start + 1;
1122
- while (count > 0) {
1123
- if (pos >= tokens.length) {
1124
- throw new LatexParserError("Unmatched curly brackets");
1125
- }
1126
- if (tokens[pos].eq(LEFT_CURLY_BRACKET)) {
1127
- count += 1;
1128
- } else if (tokens[pos].eq(RIGHT_CURLY_BRACKET)) {
1129
- count -= 1;
1130
- }
1131
- pos += 1;
1132
- }
1133
- return pos - 1;
1134
- }
1135
- function find_closing_square_bracket(tokens, start) {
1136
- assert(tokens[start].eq(LEFT_SQUARE_BRACKET));
1137
- let count = 1;
1138
- let pos = start + 1;
1139
- while (count > 0) {
1140
- if (pos >= tokens.length) {
1141
- throw new LatexParserError("Unmatched square brackets");
1142
- }
1143
- if (tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
1144
- count += 1;
1145
- } else if (tokens[pos].eq(RIGHT_SQUARE_BRACKET)) {
1146
- count -= 1;
1147
- }
1148
- pos += 1;
1149
- }
1150
- return pos - 1;
1151
- }
1152
- function isalpha(char) {
1153
- return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(char);
1154
- }
1155
- function isdigit(char) {
1156
- return "0123456789".includes(char);
1157
- }
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 */, "]");
1158
1338
  function eat_whitespaces(tokens, start) {
1159
1339
  let pos = start;
1160
1340
  while (pos < tokens.length && [4 /* SPACE */, 5 /* NEWLINE */].includes(tokens[pos].type)) {
@@ -1164,7 +1344,7 @@ function eat_whitespaces(tokens, start) {
1164
1344
  }
1165
1345
  function eat_parenthesis(tokens, start) {
1166
1346
  const firstToken = tokens[start];
1167
- if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}"].includes(firstToken.value)) {
1347
+ if (firstToken.type === 0 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", "."].includes(firstToken.value)) {
1168
1348
  return firstToken;
1169
1349
  } else if (firstToken.type === 1 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle"].includes(firstToken.value.slice(1))) {
1170
1350
  return firstToken;
@@ -1186,37 +1366,32 @@ function eat_command_name(latex, start) {
1186
1366
  }
1187
1367
  return latex.substring(start, pos);
1188
1368
  }
1189
- function find_closing_right_command(tokens, start) {
1369
+ function find_closing_match(tokens, start, leftToken, rightToken) {
1370
+ assert(tokens[start].eq(leftToken));
1190
1371
  let count = 1;
1191
- let pos = start;
1372
+ let pos = start + 1;
1192
1373
  while (count > 0) {
1193
1374
  if (pos >= tokens.length) {
1194
1375
  return -1;
1195
1376
  }
1196
- if (tokens[pos].eq(LEFT_COMMAND)) {
1377
+ if (tokens[pos].eq(leftToken)) {
1197
1378
  count += 1;
1198
- } else if (tokens[pos].eq(RIGHT_COMMAND)) {
1379
+ } else if (tokens[pos].eq(rightToken)) {
1199
1380
  count -= 1;
1200
1381
  }
1201
1382
  pos += 1;
1202
1383
  }
1203
1384
  return pos - 1;
1204
1385
  }
1386
+ var LEFT_COMMAND = new TexToken(1 /* COMMAND */, "\\left");
1387
+ var RIGHT_COMMAND = new TexToken(1 /* COMMAND */, "\\right");
1388
+ function find_closing_right_command(tokens, start) {
1389
+ return find_closing_match(tokens, start, LEFT_COMMAND, RIGHT_COMMAND);
1390
+ }
1391
+ var BEGIN_COMMAND = new TexToken(1 /* COMMAND */, "\\begin");
1392
+ var END_COMMAND = new TexToken(1 /* COMMAND */, "\\end");
1205
1393
  function find_closing_end_command(tokens, start) {
1206
- let count = 1;
1207
- let pos = start;
1208
- while (count > 0) {
1209
- if (pos >= tokens.length) {
1210
- return -1;
1211
- }
1212
- if (tokens[pos].eq(BEGIN_COMMAND)) {
1213
- count += 1;
1214
- } else if (tokens[pos].eq(END_COMMAND)) {
1215
- count -= 1;
1216
- }
1217
- pos += 1;
1218
- }
1219
- return pos - 1;
1394
+ return find_closing_match(tokens, start, BEGIN_COMMAND, END_COMMAND);
1220
1395
  }
1221
1396
  function find_closing_curly_bracket_char(latex, start) {
1222
1397
  assert(latex[start] === "{");
@@ -1248,7 +1423,8 @@ function tokenize(latex) {
1248
1423
  switch (firstChar) {
1249
1424
  case "%": {
1250
1425
  let newPos = pos + 1;
1251
- while (newPos < latex.length && latex[newPos] !== "\n") {
1426
+ while (newPos < latex.length && latex[newPos] !== `
1427
+ `) {
1252
1428
  newPos += 1;
1253
1429
  }
1254
1430
  token = new TexToken(3 /* COMMENT */, latex.slice(pos + 1, newPos));
@@ -1263,16 +1439,20 @@ function tokenize(latex) {
1263
1439
  token = new TexToken(6 /* CONTROL */, firstChar);
1264
1440
  pos++;
1265
1441
  break;
1266
- case "\n":
1442
+ case `
1443
+ `:
1267
1444
  token = new TexToken(5 /* NEWLINE */, firstChar);
1268
1445
  pos++;
1269
1446
  break;
1270
1447
  case "\r": {
1271
- if (pos + 1 < latex.length && latex[pos + 1] === "\n") {
1272
- token = new TexToken(5 /* NEWLINE */, "\n");
1448
+ if (pos + 1 < latex.length && latex[pos + 1] === `
1449
+ `) {
1450
+ token = new TexToken(5 /* NEWLINE */, `
1451
+ `);
1273
1452
  pos += 2;
1274
1453
  } else {
1275
- token = new TexToken(5 /* NEWLINE */, "\n");
1454
+ token = new TexToken(5 /* NEWLINE */, `
1455
+ `);
1276
1456
  pos++;
1277
1457
  }
1278
1458
  break;
@@ -1311,7 +1491,7 @@ function tokenize(latex) {
1311
1491
  token = new TexToken(0 /* ELEMENT */, latex.slice(pos, newPos));
1312
1492
  } else if (isalpha(firstChar)) {
1313
1493
  token = new TexToken(0 /* ELEMENT */, firstChar);
1314
- } else if ("+-*/=\'<>!.,;?()[]|".includes(firstChar)) {
1494
+ } else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
1315
1495
  token = new TexToken(0 /* ELEMENT */, firstChar);
1316
1496
  } else {
1317
1497
  token = new TexToken(7 /* UNKNOWN */, firstChar);
@@ -1339,115 +1519,22 @@ function tokenize(latex) {
1339
1519
  }
1340
1520
  return tokens;
1341
1521
  }
1342
- function passIgnoreWhitespaceBeforeScriptMark(tokens) {
1343
- const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
1344
- let out_tokens = [];
1345
- for (let i = 0;i < tokens.length; i++) {
1346
- if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
1347
- continue;
1348
- }
1349
- if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
1350
- continue;
1351
- }
1352
- out_tokens.push(tokens[i]);
1522
+
1523
+ class LatexParserError extends Error {
1524
+ constructor(message) {
1525
+ super(message);
1526
+ this.name = "LatexParserError";
1353
1527
  }
1354
- return out_tokens;
1355
1528
  }
1356
- function passExpandCustomTexMacros(tokens, customTexMacros) {
1357
- let out_tokens = [];
1358
- for (const token of tokens) {
1359
- if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
1360
- const expanded_tokens = tokenize(customTexMacros[token.value]);
1361
- out_tokens = out_tokens.concat(expanded_tokens);
1362
- } else {
1363
- out_tokens.push(token);
1364
- }
1365
- }
1366
- return out_tokens;
1367
- }
1368
- function parseTex(tex, customTexMacros) {
1369
- const parser = new LatexParser;
1370
- let tokens = tokenize(tex);
1371
- tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
1372
- tokens = passExpandCustomTexMacros(tokens, customTexMacros);
1373
- return parser.parse(tokens);
1374
- }
1375
- var UNARY_COMMANDS = [
1376
- "sqrt",
1377
- "text",
1378
- "bar",
1379
- "bold",
1380
- "boldsymbol",
1381
- "ddot",
1382
- "dot",
1383
- "hat",
1384
- "mathbb",
1385
- "mathbf",
1386
- "mathcal",
1387
- "mathfrak",
1388
- "mathit",
1389
- "mathrm",
1390
- "mathscr",
1391
- "mathsf",
1392
- "mathtt",
1393
- "operatorname",
1394
- "overbrace",
1395
- "overline",
1396
- "pmb",
1397
- "rm",
1398
- "tilde",
1399
- "underbrace",
1400
- "underline",
1401
- "vec",
1402
- "widehat",
1403
- "widetilde"
1404
- ];
1405
- var BINARY_COMMANDS = [
1406
- "frac",
1407
- "tfrac",
1408
- "binom",
1409
- "dbinom",
1410
- "dfrac",
1411
- "tbinom",
1412
- "overset"
1413
- ];
1414
-
1415
- class TexToken {
1416
- type;
1417
- value;
1418
- constructor(type, value) {
1419
- this.type = type;
1420
- this.value = value;
1421
- }
1422
- eq(token) {
1423
- return this.type === token.type && this.value === token.value;
1424
- }
1425
- }
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");
1435
-
1436
- class LatexParserError extends Error {
1437
- constructor(message) {
1438
- super(message);
1439
- this.name = "LatexParserError";
1440
- }
1441
- }
1442
- var SUB_SYMBOL = new TexToken(6 /* CONTROL */, "_");
1443
- var SUP_SYMBOL = new TexToken(6 /* CONTROL */, "^");
1444
-
1445
- class LatexParser {
1446
- space_sensitive;
1447
- newline_sensitive;
1448
- constructor(space_sensitive = false, newline_sensitive = true) {
1449
- this.space_sensitive = space_sensitive;
1450
- this.newline_sensitive = newline_sensitive;
1529
+ var SUB_SYMBOL = new TexToken(6 /* CONTROL */, "_");
1530
+ var SUP_SYMBOL = new TexToken(6 /* CONTROL */, "^");
1531
+
1532
+ class LatexParser {
1533
+ space_sensitive;
1534
+ newline_sensitive;
1535
+ constructor(space_sensitive = false, newline_sensitive = true) {
1536
+ this.space_sensitive = space_sensitive;
1537
+ this.newline_sensitive = newline_sensitive;
1451
1538
  }
1452
1539
  parse(tokens) {
1453
1540
  const results = [];
@@ -1462,7 +1549,8 @@ class LatexParser {
1462
1549
  if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
1463
1550
  continue;
1464
1551
  }
1465
- if (!this.newline_sensitive && res.content === "\n") {
1552
+ if (!this.newline_sensitive && res.content === `
1553
+ `) {
1466
1554
  continue;
1467
1555
  }
1468
1556
  }
@@ -1565,7 +1653,7 @@ class LatexParser {
1565
1653
  const controlChar = firstToken.value;
1566
1654
  switch (controlChar) {
1567
1655
  case "{":
1568
- const posClosingBracket = find_closing_curly_bracket(tokens, start);
1656
+ const posClosingBracket = find_closing_match(tokens, start, LEFT_CURLY_BRACKET, RIGHT_CURLY_BRACKET);
1569
1657
  const exprInside = tokens.slice(start + 1, posClosingBracket);
1570
1658
  return [this.parse(exprInside), posClosingBracket + 1];
1571
1659
  case "}":
@@ -1574,12 +1662,9 @@ class LatexParser {
1574
1662
  return [new TexNode("control", "\\\\"), start + 1];
1575
1663
  case "\\,":
1576
1664
  return [new TexNode("control", "\\,"), start + 1];
1577
- case "_": {
1578
- return [EMPTY_NODE, start];
1579
- }
1580
- case "^": {
1665
+ case "_":
1666
+ case "^":
1581
1667
  return [EMPTY_NODE, start];
1582
- }
1583
1668
  case "&":
1584
1669
  return [new TexNode("control", "&"), start + 1];
1585
1670
  default:
@@ -1606,7 +1691,7 @@ class LatexParser {
1606
1691
  case 1: {
1607
1692
  if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
1608
1693
  const posLeftSquareBracket = pos;
1609
- const posRightSquareBracket = find_closing_square_bracket(tokens, pos);
1694
+ const posRightSquareBracket = find_closing_match(tokens, pos, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET);
1610
1695
  const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
1611
1696
  const exponent = this.parse(exprInside);
1612
1697
  const [arg12, newPos2] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
@@ -1646,7 +1731,7 @@ class LatexParser {
1646
1731
  }
1647
1732
  pos++;
1648
1733
  const exprInsideStart = pos;
1649
- const idx = find_closing_right_command(tokens, pos);
1734
+ const idx = find_closing_right_command(tokens, start);
1650
1735
  if (idx === -1) {
1651
1736
  throw new LatexParserError("No matching \\right");
1652
1737
  }
@@ -1681,7 +1766,7 @@ class LatexParser {
1681
1766
  pos += 3;
1682
1767
  pos += eat_whitespaces(tokens, pos).length;
1683
1768
  const exprInsideStart = pos;
1684
- const endIdx = find_closing_end_command(tokens, pos);
1769
+ const endIdx = find_closing_end_command(tokens, start);
1685
1770
  if (endIdx === -1) {
1686
1771
  throw new LatexParserError("No matching \\end");
1687
1772
  }
@@ -1716,7 +1801,8 @@ class LatexParser {
1716
1801
  if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
1717
1802
  continue;
1718
1803
  }
1719
- if (!this.newline_sensitive && res.content === "\n") {
1804
+ if (!this.newline_sensitive && res.content === `
1805
+ `) {
1720
1806
  continue;
1721
1807
  }
1722
1808
  }
@@ -1735,15 +1821,57 @@ class LatexParser {
1735
1821
  return allRows;
1736
1822
  }
1737
1823
  }
1824
+ function passIgnoreWhitespaceBeforeScriptMark(tokens) {
1825
+ const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
1826
+ let out_tokens = [];
1827
+ for (let i = 0;i < tokens.length; i++) {
1828
+ if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
1829
+ continue;
1830
+ }
1831
+ if (tokens[i].type === 4 /* SPACE */ && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
1832
+ continue;
1833
+ }
1834
+ out_tokens.push(tokens[i]);
1835
+ }
1836
+ return out_tokens;
1837
+ }
1838
+ function passExpandCustomTexMacros(tokens, customTexMacros) {
1839
+ let out_tokens = [];
1840
+ for (const token of tokens) {
1841
+ if (token.type === 1 /* COMMAND */ && customTexMacros[token.value]) {
1842
+ const expanded_tokens = tokenize(customTexMacros[token.value]);
1843
+ out_tokens = out_tokens.concat(expanded_tokens);
1844
+ } else {
1845
+ out_tokens.push(token);
1846
+ }
1847
+ }
1848
+ return out_tokens;
1849
+ }
1850
+ function parseTex(tex, customTexMacros) {
1851
+ const parser = new LatexParser;
1852
+ let tokens = tokenize(tex);
1853
+ tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
1854
+ tokens = passExpandCustomTexMacros(tokens, customTexMacros);
1855
+ return parser.parse(tokens);
1856
+ }
1738
1857
 
1739
1858
  // src/writer.ts
1859
+ var TYPST_INTRINSIC_SYMBOLS = [
1860
+ "dim",
1861
+ "id",
1862
+ "im",
1863
+ "mod",
1864
+ "Pr",
1865
+ "sech",
1866
+ "csch"
1867
+ ];
1740
1868
  function is_delimiter(c) {
1741
- return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
1869
+ return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "", "", "", ""].includes(c.content);
1742
1870
  }
1743
1871
  function convert_overset(node) {
1744
1872
  const [sup, base] = node.args;
1745
1873
  const is_def = (n) => {
1746
- if (n.eq_shallow(new TexNode("text", "def"))) {
1874
+ if (n.eq(new TexNode("text", "def"))) {
1747
1875
  return true;
1748
1876
  }
1749
1877
  if (n.type === "ordgroup" && n.args.length === 3) {
@@ -1751,169 +1879,28 @@ function convert_overset(node) {
1751
1879
  const d = new TexNode("element", "d");
1752
1880
  const e = new TexNode("element", "e");
1753
1881
  const f = new TexNode("element", "f");
1754
- if (a1.eq_shallow(d) && a2.eq_shallow(e) && a3.eq_shallow(f)) {
1882
+ if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
1755
1883
  return true;
1756
1884
  }
1757
1885
  }
1758
1886
  return false;
1759
1887
  };
1760
- const is_eq = (n) => n.eq_shallow(new TexNode("element", "="));
1888
+ const is_eq = (n) => n.eq(new TexNode("element", "="));
1761
1889
  if (is_def(sup) && is_eq(base)) {
1762
1890
  return new TypstNode("symbol", "eq.def");
1763
1891
  }
1764
- const op_call = new TypstNode("unaryFunc", "op", [convertTree(base)]);
1892
+ const op_call = new TypstNode("funcCall", "op", [convertTree(base)]);
1765
1893
  op_call.setOptions({ limits: "#true" });
1766
1894
  return new TypstNode("supsub", "", [], {
1767
1895
  base: op_call,
1768
1896
  sup: convertTree(sup)
1769
1897
  });
1770
1898
  }
1771
- function convertTree(node) {
1772
- switch (node.type) {
1773
- case "empty":
1774
- return new TypstNode("empty", "");
1775
- case "whitespace":
1776
- return new TypstNode("whitespace", node.content);
1777
- case "ordgroup":
1778
- return new TypstNode("group", "", node.args.map(convertTree));
1779
- case "element":
1780
- return new TypstNode("atom", convertToken(node.content));
1781
- case "symbol":
1782
- return new TypstNode("symbol", convertToken(node.content));
1783
- case "text":
1784
- return new TypstNode("text", node.content);
1785
- case "comment":
1786
- return new TypstNode("comment", node.content);
1787
- case "supsub": {
1788
- let { base, sup, sub } = node.data;
1789
- if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
1790
- return new TypstNode("binaryFunc", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
1791
- } else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
1792
- return new TypstNode("binaryFunc", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
1793
- }
1794
- const data = {
1795
- base: convertTree(base)
1796
- };
1797
- if (data.base.type === "empty") {
1798
- data.base = new TypstNode("text", "");
1799
- }
1800
- if (sup) {
1801
- data.sup = convertTree(sup);
1802
- }
1803
- if (sub) {
1804
- data.sub = convertTree(sub);
1805
- }
1806
- return new TypstNode("supsub", "", [], data);
1807
- }
1808
- case "leftright": {
1809
- const [left, body, right] = node.args;
1810
- const group = new TypstNode("group", "", node.args.map(convertTree));
1811
- if ([
1812
- "[]",
1813
- "()",
1814
- "\\{\\}",
1815
- "\\lfloor\\rfloor",
1816
- "\\lceil\\rceil",
1817
- "\\lfloor\\rceil"
1818
- ].includes(left.content + right.content)) {
1819
- return group;
1820
- }
1821
- return new TypstNode("unaryFunc", "lr", [group]);
1822
- }
1823
- case "binaryFunc": {
1824
- if (node.content === "\\overset") {
1825
- return convert_overset(node);
1826
- }
1827
- return new TypstNode("binaryFunc", convertToken(node.content), node.args.map(convertTree));
1828
- }
1829
- case "unaryFunc": {
1830
- const arg0 = convertTree(node.args[0]);
1831
- if (node.content === "\\sqrt" && node.data) {
1832
- const data = convertTree(node.data);
1833
- return new TypstNode("binaryFunc", "root", [data, arg0]);
1834
- }
1835
- if (node.content === "\\mathbf") {
1836
- const inner = new TypstNode("unaryFunc", "bold", [arg0]);
1837
- return new TypstNode("unaryFunc", "upright", [inner]);
1838
- }
1839
- if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
1840
- return new TypstNode("symbol", arg0.content + arg0.content);
1841
- }
1842
- if (node.content === "\\operatorname") {
1843
- const body = node.args;
1844
- if (body.length !== 1 || body[0].type !== "text") {
1845
- throw new TypstWriterError(`Expecting body of \\operatorname to be text but got`, node);
1846
- }
1847
- const text = body[0].content;
1848
- if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
1849
- return new TypstNode("symbol", text);
1850
- } else {
1851
- return new TypstNode("unaryFunc", "op", [new TypstNode("text", text)]);
1852
- }
1853
- }
1854
- return new TypstNode("unaryFunc", convertToken(node.content), node.args.map(convertTree));
1855
- }
1856
- case "beginend": {
1857
- const matrix = node.data;
1858
- const data = matrix.map((row) => row.map(convertTree));
1859
- if (node.content.startsWith("align")) {
1860
- return new TypstNode("align", "", [], data);
1861
- } else {
1862
- const res = new TypstNode("matrix", "", [], data);
1863
- res.setOptions({ delim: "#none" });
1864
- return res;
1865
- }
1866
- }
1867
- case "unknownMacro":
1868
- return new TypstNode("unknown", convertToken(node.content));
1869
- case "control":
1870
- if (node.content === "\\\\") {
1871
- return new TypstNode("symbol", "\\");
1872
- } else if (node.content === "\\,") {
1873
- return new TypstNode("symbol", "thin");
1874
- } else {
1875
- throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
1876
- }
1877
- default:
1878
- throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
1879
- }
1880
- }
1881
- function convertToken(token) {
1882
- if (/^[a-zA-Z0-9]$/.test(token)) {
1883
- return token;
1884
- } else if (token === "/") {
1885
- return "\\/";
1886
- } else if (token === "\\|") {
1887
- return "parallel";
1888
- } else if (token === "\\colon") {
1889
- return ":";
1890
- } else if (token === "\\\\") {
1891
- return "\\";
1892
- } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
1893
- return token;
1894
- } else if (token.startsWith("\\")) {
1895
- const symbol = token.slice(1);
1896
- if (symbolMap.has(symbol)) {
1897
- return symbolMap.get(symbol);
1898
- } else {
1899
- return symbol;
1900
- }
1901
- }
1902
- return token;
1903
- }
1904
- var TYPST_INTRINSIC_SYMBOLS = [
1905
- "dim",
1906
- "id",
1907
- "im",
1908
- "mod",
1909
- "Pr",
1910
- "sech",
1911
- "csch"
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");
1899
+ var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
1900
+ var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
1901
+ var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
1902
+ var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, `
1903
+ `);
1917
1904
 
1918
1905
  class TypstWriterError extends Error {
1919
1906
  node;
@@ -1937,19 +1924,22 @@ class TypstWriter {
1937
1924
  this.keepSpaces = keepSpaces;
1938
1925
  }
1939
1926
  writeBuffer(token) {
1940
- const str = token.content;
1927
+ const str = token.toString();
1941
1928
  if (str === "") {
1942
1929
  return;
1943
1930
  }
1944
1931
  let no_need_space = false;
1945
- no_need_space ||= /[\(\|]$/.test(this.buffer) && /^\w/.test(str);
1946
- no_need_space ||= /^[}()_^,;!\|]$/.test(str);
1932
+ no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
1933
+ no_need_space ||= /^[})\]\|]$/.test(str);
1934
+ no_need_space ||= /^[(_^,;!]$/.test(str);
1947
1935
  no_need_space ||= str === "'";
1948
1936
  no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
1949
1937
  no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
1950
- no_need_space ||= str.startsWith("\n");
1938
+ no_need_space ||= str.startsWith(`
1939
+ `);
1951
1940
  no_need_space ||= this.buffer === "";
1952
1941
  no_need_space ||= /^\s/.test(str);
1942
+ no_need_space ||= this.buffer.endsWith("&") && str === "=";
1953
1943
  no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
1954
1944
  if (!no_need_space) {
1955
1945
  this.buffer += " ";
@@ -1964,7 +1954,7 @@ class TypstWriter {
1964
1954
  if (node.content === "," && this.insideFunctionDepth > 0) {
1965
1955
  this.queue.push(new TypstToken(0 /* SYMBOL */, "comma"));
1966
1956
  } else {
1967
- this.queue.push(new TypstToken(1 /* ATOM */, node.content));
1957
+ this.queue.push(new TypstToken(1 /* ELEMENT */, node.content));
1968
1958
  }
1969
1959
  break;
1970
1960
  }
@@ -1972,10 +1962,10 @@ class TypstWriter {
1972
1962
  this.queue.push(new TypstToken(0 /* SYMBOL */, node.content));
1973
1963
  break;
1974
1964
  case "text":
1975
- this.queue.push(new TypstToken(2 /* TEXT */, `"${node.content}"`));
1965
+ this.queue.push(new TypstToken(2 /* TEXT */, node.content));
1976
1966
  break;
1977
1967
  case "comment":
1978
- this.queue.push(new TypstToken(3 /* COMMENT */, `//${node.content}`));
1968
+ this.queue.push(new TypstToken(3 /* COMMENT */, node.content));
1979
1969
  break;
1980
1970
  case "whitespace":
1981
1971
  for (const c of node.content) {
@@ -1983,7 +1973,8 @@ class TypstWriter {
1983
1973
  if (this.keepSpaces) {
1984
1974
  this.queue.push(new TypstToken(4 /* SPACE */, c));
1985
1975
  }
1986
- } else if (c === "\n") {
1976
+ } else if (c === `
1977
+ `) {
1987
1978
  this.queue.push(new TypstToken(0 /* SYMBOL */, c));
1988
1979
  } else {
1989
1980
  throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
@@ -1999,17 +1990,17 @@ class TypstWriter {
1999
1990
  let { base, sup, sub } = node.data;
2000
1991
  this.appendWithBracketsIfNeeded(base);
2001
1992
  let trailing_space_needed = false;
2002
- const has_prime = sup && sup.type === "atom" && sup.content === "\'";
1993
+ const has_prime = sup && sup.type === "atom" && sup.content === "'";
2003
1994
  if (has_prime) {
2004
- this.queue.push(new TypstToken(1 /* ATOM */, "\'"));
1995
+ this.queue.push(new TypstToken(1 /* ELEMENT */, "'"));
2005
1996
  trailing_space_needed = false;
2006
1997
  }
2007
1998
  if (sub) {
2008
- this.queue.push(new TypstToken(1 /* ATOM */, "_"));
1999
+ this.queue.push(new TypstToken(1 /* ELEMENT */, "_"));
2009
2000
  trailing_space_needed = this.appendWithBracketsIfNeeded(sub);
2010
2001
  }
2011
2002
  if (sup && !has_prime) {
2012
- this.queue.push(new TypstToken(1 /* ATOM */, "^"));
2003
+ this.queue.push(new TypstToken(1 /* ELEMENT */, "^"));
2013
2004
  trailing_space_needed = this.appendWithBracketsIfNeeded(sup);
2014
2005
  }
2015
2006
  if (trailing_space_needed) {
@@ -2017,26 +2008,17 @@ class TypstWriter {
2017
2008
  }
2018
2009
  break;
2019
2010
  }
2020
- case "binaryFunc": {
2021
- const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
2022
- const [arg0, arg1] = node.args;
2023
- this.queue.push(func_symbol);
2024
- this.insideFunctionDepth++;
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);
2030
- this.insideFunctionDepth--;
2031
- break;
2032
- }
2033
- case "unaryFunc": {
2011
+ case "funcCall": {
2034
2012
  const func_symbol = new TypstToken(0 /* SYMBOL */, node.content);
2035
- const arg0 = node.args[0];
2036
2013
  this.queue.push(func_symbol);
2037
2014
  this.insideFunctionDepth++;
2038
2015
  this.queue.push(TYPST_LEFT_PARENTHESIS);
2039
- this.serialize(arg0);
2016
+ for (let i = 0;i < node.args.length; i++) {
2017
+ this.serialize(node.args[i]);
2018
+ if (i < node.args.length - 1) {
2019
+ this.queue.push(new TypstToken(1 /* ELEMENT */, ","));
2020
+ }
2021
+ }
2040
2022
  if (node.options) {
2041
2023
  for (const [key, value] of Object.entries(node.options)) {
2042
2024
  this.queue.push(new TypstToken(0 /* SYMBOL */, `, ${key}: ${value}`));
@@ -2051,7 +2033,7 @@ class TypstWriter {
2051
2033
  matrix.forEach((row, i) => {
2052
2034
  row.forEach((cell, j) => {
2053
2035
  if (j > 0) {
2054
- this.queue.push(new TypstToken(1 /* ATOM */, "&"));
2036
+ this.queue.push(new TypstToken(1 /* ELEMENT */, "&"));
2055
2037
  }
2056
2038
  this.serialize(cell);
2057
2039
  });
@@ -2075,10 +2057,10 @@ class TypstWriter {
2075
2057
  row.forEach((cell, j) => {
2076
2058
  this.serialize(cell);
2077
2059
  if (j < row.length - 1) {
2078
- this.queue.push(new TypstToken(1 /* ATOM */, ","));
2060
+ this.queue.push(new TypstToken(1 /* ELEMENT */, ","));
2079
2061
  } else {
2080
2062
  if (i < matrix.length - 1) {
2081
- this.queue.push(new TypstToken(1 /* ATOM */, ";"));
2063
+ this.queue.push(new TypstToken(1 /* ELEMENT */, ";"));
2082
2064
  }
2083
2065
  }
2084
2066
  });
@@ -2123,9 +2105,9 @@ class TypstWriter {
2123
2105
  let token = this.queue[i];
2124
2106
  if (token.eq(SOFT_SPACE)) {
2125
2107
  if (i === this.queue.length - 1) {
2126
- this.queue[i].content = "";
2108
+ this.queue[i].value = "";
2127
2109
  } else if (this.queue[i + 1].isOneOf([TYPST_RIGHT_PARENTHESIS, TYPST_COMMA, TYPST_NEWLINE])) {
2128
- this.queue[i].content = "";
2110
+ this.queue[i].value = "";
2129
2111
  }
2130
2112
  }
2131
2113
  }
@@ -2158,7 +2140,810 @@ class TypstWriter {
2158
2140
  return this.buffer;
2159
2141
  }
2160
2142
  }
2161
-
2143
+ function convertTree(node) {
2144
+ switch (node.type) {
2145
+ case "empty":
2146
+ return new TypstNode("empty", "");
2147
+ case "whitespace":
2148
+ return new TypstNode("whitespace", node.content);
2149
+ case "ordgroup":
2150
+ return new TypstNode("group", "", node.args.map(convertTree));
2151
+ case "element":
2152
+ return new TypstNode("atom", convertToken(node.content));
2153
+ case "symbol":
2154
+ return new TypstNode("symbol", convertToken(node.content));
2155
+ case "text":
2156
+ return new TypstNode("text", node.content);
2157
+ case "comment":
2158
+ return new TypstNode("comment", node.content);
2159
+ case "supsub": {
2160
+ let { base, sup, sub } = node.data;
2161
+ if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
2162
+ return new TypstNode("funcCall", "overbrace", [convertTree(base.args[0]), convertTree(sup)]);
2163
+ } else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
2164
+ return new TypstNode("funcCall", "underbrace", [convertTree(base.args[0]), convertTree(sub)]);
2165
+ }
2166
+ const data = {
2167
+ base: convertTree(base)
2168
+ };
2169
+ if (data.base.type === "empty") {
2170
+ data.base = new TypstNode("text", "");
2171
+ }
2172
+ if (sup) {
2173
+ data.sup = convertTree(sup);
2174
+ }
2175
+ if (sub) {
2176
+ data.sub = convertTree(sub);
2177
+ }
2178
+ return new TypstNode("supsub", "", [], data);
2179
+ }
2180
+ case "leftright": {
2181
+ const [left, body, right] = node.args;
2182
+ const group = new TypstNode("group", "", node.args.map(convertTree));
2183
+ if ([
2184
+ "[]",
2185
+ "()",
2186
+ "\\{\\}",
2187
+ "\\lfloor\\rfloor",
2188
+ "\\lceil\\rceil",
2189
+ "\\lfloor\\rceil"
2190
+ ].includes(left.content + right.content)) {
2191
+ return group;
2192
+ }
2193
+ if (right.content === ".") {
2194
+ group.args.pop();
2195
+ return group;
2196
+ } else if (left.content === ".") {
2197
+ group.args.shift();
2198
+ return new TypstNode("funcCall", "lr", [group]);
2199
+ }
2200
+ return new TypstNode("funcCall", "lr", [group]);
2201
+ }
2202
+ case "binaryFunc": {
2203
+ if (node.content === "\\overset") {
2204
+ return convert_overset(node);
2205
+ }
2206
+ return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
2207
+ }
2208
+ case "unaryFunc": {
2209
+ const arg0 = convertTree(node.args[0]);
2210
+ if (node.content === "\\sqrt" && node.data) {
2211
+ const data = convertTree(node.data);
2212
+ return new TypstNode("funcCall", "root", [data, arg0]);
2213
+ }
2214
+ if (node.content === "\\mathbf") {
2215
+ const inner = new TypstNode("funcCall", "bold", [arg0]);
2216
+ return new TypstNode("funcCall", "upright", [inner]);
2217
+ }
2218
+ if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
2219
+ return new TypstNode("symbol", arg0.content + arg0.content);
2220
+ }
2221
+ if (node.content === "\\operatorname") {
2222
+ const body = node.args;
2223
+ if (body.length !== 1 || body[0].type !== "text") {
2224
+ throw new TypstWriterError(`Expecting body of \\operatorname to be text but got`, node);
2225
+ }
2226
+ const text = body[0].content;
2227
+ if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
2228
+ return new TypstNode("symbol", text);
2229
+ } else {
2230
+ return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
2231
+ }
2232
+ }
2233
+ return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
2234
+ }
2235
+ case "beginend": {
2236
+ const matrix = node.data;
2237
+ const data = matrix.map((row) => row.map(convertTree));
2238
+ if (node.content.startsWith("align")) {
2239
+ return new TypstNode("align", "", [], data);
2240
+ } else {
2241
+ const res = new TypstNode("matrix", "", [], data);
2242
+ res.setOptions({ delim: "#none" });
2243
+ return res;
2244
+ }
2245
+ }
2246
+ case "unknownMacro":
2247
+ return new TypstNode("unknown", convertToken(node.content));
2248
+ case "control":
2249
+ if (node.content === "\\\\") {
2250
+ return new TypstNode("symbol", "\\");
2251
+ } else if (node.content === "\\,") {
2252
+ return new TypstNode("symbol", "thin");
2253
+ } else {
2254
+ throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
2255
+ }
2256
+ default:
2257
+ throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
2258
+ }
2259
+ }
2260
+ function convertToken(token) {
2261
+ if (/^[a-zA-Z0-9]$/.test(token)) {
2262
+ return token;
2263
+ } else if (token === "/") {
2264
+ return "\\/";
2265
+ } else if (token === "\\|") {
2266
+ return "parallel";
2267
+ } else if (token === "\\colon") {
2268
+ return ":";
2269
+ } else if (token === "\\\\") {
2270
+ return "\\";
2271
+ } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
2272
+ return token;
2273
+ } else if (token.startsWith("\\")) {
2274
+ const symbol = token.slice(1);
2275
+ if (symbolMap.has(symbol)) {
2276
+ return symbolMap.get(symbol);
2277
+ } else {
2278
+ return symbol;
2279
+ }
2280
+ }
2281
+ return token;
2282
+ }
2283
+
2284
+ // src/typst-parser.ts
2285
+ function eat_primes2(tokens, start) {
2286
+ let pos = start;
2287
+ while (pos < tokens.length && tokens[pos].eq(new TypstToken(1 /* ELEMENT */, "'"))) {
2288
+ pos += 1;
2289
+ }
2290
+ return pos - start;
2291
+ }
2292
+ function eat_identifier_name(typst, start) {
2293
+ let pos = start;
2294
+ while (pos < typst.length && (isalpha(typst[pos]) || typst[pos] === ".")) {
2295
+ pos += 1;
2296
+ }
2297
+ return typst.substring(start, pos);
2298
+ }
2299
+ var TYPST_EMPTY_NODE = new TypstNode("empty", "");
2300
+ function tokenize_typst(typst) {
2301
+ const tokens = [];
2302
+ let pos = 0;
2303
+ while (pos < typst.length) {
2304
+ const firstChar = typst[pos];
2305
+ let token;
2306
+ switch (firstChar) {
2307
+ case "_":
2308
+ case "^":
2309
+ case "&":
2310
+ token = new TypstToken(6 /* CONTROL */, firstChar);
2311
+ pos++;
2312
+ break;
2313
+ case `
2314
+ `:
2315
+ token = new TypstToken(7 /* NEWLINE */, firstChar);
2316
+ pos++;
2317
+ break;
2318
+ case "\r": {
2319
+ if (pos + 1 < typst.length && typst[pos + 1] === `
2320
+ `) {
2321
+ token = new TypstToken(7 /* NEWLINE */, `
2322
+ `);
2323
+ pos += 2;
2324
+ } else {
2325
+ token = new TypstToken(7 /* NEWLINE */, `
2326
+ `);
2327
+ pos++;
2328
+ }
2329
+ break;
2330
+ }
2331
+ case " ": {
2332
+ let newPos = pos;
2333
+ while (newPos < typst.length && typst[newPos] === " ") {
2334
+ newPos++;
2335
+ }
2336
+ token = new TypstToken(4 /* SPACE */, typst.substring(pos, newPos));
2337
+ pos = newPos;
2338
+ break;
2339
+ }
2340
+ case "/": {
2341
+ if (pos < typst.length && typst[pos + 1] === "/") {
2342
+ let newPos = pos + 2;
2343
+ while (newPos < typst.length && typst[newPos] !== `
2344
+ `) {
2345
+ newPos++;
2346
+ }
2347
+ token = new TypstToken(3 /* COMMENT */, typst.slice(pos + 2, newPos));
2348
+ pos = newPos;
2349
+ } else {
2350
+ token = new TypstToken(1 /* ELEMENT */, "/");
2351
+ pos++;
2352
+ }
2353
+ break;
2354
+ }
2355
+ case "\\": {
2356
+ if (pos + 1 >= typst.length) {
2357
+ throw new Error("Expecting a character after \\");
2358
+ }
2359
+ const firstTwoChars = typst.substring(pos, pos + 2);
2360
+ if (["\\$", "\\&", "\\#", "\\_"].includes(firstTwoChars)) {
2361
+ token = new TypstToken(1 /* ELEMENT */, firstTwoChars);
2362
+ pos += 2;
2363
+ } else if (firstTwoChars === "\\\n") {
2364
+ token = new TypstToken(6 /* CONTROL */, "\\");
2365
+ pos += 1;
2366
+ } else {
2367
+ token = new TypstToken(6 /* CONTROL */, "");
2368
+ pos++;
2369
+ }
2370
+ break;
2371
+ }
2372
+ case '"': {
2373
+ let newPos = pos + 1;
2374
+ while (newPos < typst.length) {
2375
+ if (typst[newPos] === '"' && typst[newPos - 1] !== "\\") {
2376
+ break;
2377
+ }
2378
+ newPos++;
2379
+ }
2380
+ let text = typst.substring(pos + 1, newPos);
2381
+ const chars = ['"', "\\"];
2382
+ for (const char of chars) {
2383
+ text = text.replaceAll("\\" + char, char);
2384
+ }
2385
+ token = new TypstToken(2 /* TEXT */, text);
2386
+ pos = newPos + 1;
2387
+ break;
2388
+ }
2389
+ default: {
2390
+ if (isdigit(firstChar)) {
2391
+ let newPos = pos;
2392
+ while (newPos < typst.length && isdigit(typst[newPos])) {
2393
+ newPos += 1;
2394
+ }
2395
+ token = new TypstToken(1 /* ELEMENT */, typst.slice(pos, newPos));
2396
+ } else if ("+-*/='<>!.,;?()[]|".includes(firstChar)) {
2397
+ token = new TypstToken(1 /* ELEMENT */, firstChar);
2398
+ } else if (isalpha(firstChar)) {
2399
+ const identifier = eat_identifier_name(typst, pos);
2400
+ const _type = identifier.length === 1 ? 1 /* ELEMENT */ : 0 /* SYMBOL */;
2401
+ token = new TypstToken(_type, identifier);
2402
+ } else {
2403
+ token = new TypstToken(1 /* ELEMENT */, firstChar);
2404
+ }
2405
+ pos += token.value.length;
2406
+ }
2407
+ }
2408
+ tokens.push(token);
2409
+ }
2410
+ return tokens;
2411
+ }
2412
+ function find_closing_match2(tokens, start) {
2413
+ assert(tokens[start].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2]));
2414
+ let count = 1;
2415
+ let pos = start + 1;
2416
+ while (count > 0) {
2417
+ if (pos >= tokens.length) {
2418
+ throw new Error("Unmatched brackets");
2419
+ }
2420
+ if (tokens[pos].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2])) {
2421
+ count += 1;
2422
+ } else if (tokens[pos].isOneOf([RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2])) {
2423
+ count -= 1;
2424
+ }
2425
+ pos += 1;
2426
+ }
2427
+ return pos - 1;
2428
+ }
2429
+ function find_closing_parenthesis(nodes, start) {
2430
+ const left_parenthesis = new TypstNode("atom", "(");
2431
+ const right_parenthesis = new TypstNode("atom", ")");
2432
+ assert(nodes[start].eq(left_parenthesis));
2433
+ let count = 1;
2434
+ let pos = start + 1;
2435
+ while (count > 0) {
2436
+ if (pos >= nodes.length) {
2437
+ throw new Error("Unmatched brackets");
2438
+ }
2439
+ if (nodes[pos].eq(left_parenthesis)) {
2440
+ count += 1;
2441
+ } else if (nodes[pos].eq(right_parenthesis)) {
2442
+ count -= 1;
2443
+ }
2444
+ pos += 1;
2445
+ }
2446
+ return pos - 1;
2447
+ }
2448
+ function primes(num) {
2449
+ const res = [];
2450
+ for (let i = 0;i < num; i++) {
2451
+ res.push(new TypstNode("atom", "'"));
2452
+ }
2453
+ return res;
2454
+ }
2455
+ var DIV = new TypstNode("atom", "/");
2456
+ function next_non_whitespace(nodes, start) {
2457
+ let pos = start;
2458
+ while (pos < nodes.length && nodes[pos].type === "whitespace") {
2459
+ pos++;
2460
+ }
2461
+ return pos === nodes.length ? TYPST_EMPTY_NODE : nodes[pos];
2462
+ }
2463
+ function trim_whitespace_around_operators(nodes) {
2464
+ let after_operator = false;
2465
+ const res = [];
2466
+ for (let i = 0;i < nodes.length; i++) {
2467
+ const current = nodes[i];
2468
+ if (current.type === "whitespace") {
2469
+ if (after_operator) {
2470
+ continue;
2471
+ }
2472
+ if (next_non_whitespace(nodes, i + 1).eq(DIV)) {
2473
+ continue;
2474
+ }
2475
+ }
2476
+ if (current.eq(DIV)) {
2477
+ after_operator = true;
2478
+ } else {
2479
+ after_operator = false;
2480
+ }
2481
+ res.push(current);
2482
+ }
2483
+ return res;
2484
+ }
2485
+ function process_operators(nodes, parenthesis = false) {
2486
+ nodes = trim_whitespace_around_operators(nodes);
2487
+ const opening_bracket = new TypstNode("atom", "(");
2488
+ const closing_bracket = new TypstNode("atom", ")");
2489
+ const stack = [];
2490
+ const args = [];
2491
+ let pos = 0;
2492
+ while (pos < nodes.length) {
2493
+ const current = nodes[pos];
2494
+ if (current.eq(closing_bracket)) {
2495
+ throw new TypstParserError("Unexpected ')'");
2496
+ } else if (current.eq(DIV)) {
2497
+ stack.push(current);
2498
+ pos++;
2499
+ } else {
2500
+ let current_tree;
2501
+ if (current.eq(opening_bracket)) {
2502
+ const pos_closing = find_closing_parenthesis(nodes, pos);
2503
+ current_tree = process_operators(nodes.slice(pos + 1, pos_closing), true);
2504
+ pos = pos_closing + 1;
2505
+ } else {
2506
+ current_tree = current;
2507
+ pos++;
2508
+ }
2509
+ if (stack.length > 0 && stack[stack.length - 1].eq(DIV)) {
2510
+ const denominator = current_tree;
2511
+ if (args.length === 0) {
2512
+ throw new TypstParserError("Unexpected '/' operator, no numerator before it");
2513
+ }
2514
+ const numerator = args.pop();
2515
+ if (denominator.type === "group" && denominator.content === "parenthesis") {
2516
+ denominator.content = "";
2517
+ }
2518
+ if (numerator.type === "group" && numerator.content === "parenthesis") {
2519
+ numerator.content = "";
2520
+ }
2521
+ args.push(new TypstNode("fraction", "", [numerator, denominator]));
2522
+ stack.pop();
2523
+ } else {
2524
+ args.push(current_tree);
2525
+ }
2526
+ }
2527
+ }
2528
+ if (parenthesis) {
2529
+ return new TypstNode("group", "parenthesis", args);
2530
+ } else {
2531
+ if (args.length === 0) {
2532
+ return TYPST_EMPTY_NODE;
2533
+ } else if (args.length === 1) {
2534
+ return args[0];
2535
+ } else {
2536
+ return new TypstNode("group", "", args);
2537
+ }
2538
+ }
2539
+ }
2540
+
2541
+ class TypstParserError extends Error {
2542
+ constructor(message) {
2543
+ super(message);
2544
+ this.name = "TypstParserError";
2545
+ }
2546
+ }
2547
+ var SUB_SYMBOL2 = new TypstToken(6 /* CONTROL */, "_");
2548
+ var SUP_SYMBOL2 = new TypstToken(6 /* CONTROL */, "^");
2549
+ var LEFT_PARENTHESES = new TypstToken(1 /* ELEMENT */, "(");
2550
+ var RIGHT_PARENTHESES = new TypstToken(1 /* ELEMENT */, ")");
2551
+ var LEFT_BRACKET = new TypstToken(1 /* ELEMENT */, "[");
2552
+ var RIGHT_BRACKET = new TypstToken(1 /* ELEMENT */, "]");
2553
+ var LEFT_CURLY_BRACKET2 = new TypstToken(1 /* ELEMENT */, "{");
2554
+ var RIGHT_CURLY_BRACKET2 = new TypstToken(1 /* ELEMENT */, "}");
2555
+ var COMMA = new TypstToken(1 /* ELEMENT */, ",");
2556
+ var SEMICOLON = new TypstToken(1 /* ELEMENT */, ";");
2557
+ var SINGLE_SPACE = new TypstToken(4 /* SPACE */, " ");
2558
+
2559
+ class TypstParser {
2560
+ space_sensitive;
2561
+ newline_sensitive;
2562
+ constructor(space_sensitive = true, newline_sensitive = true) {
2563
+ this.space_sensitive = space_sensitive;
2564
+ this.newline_sensitive = newline_sensitive;
2565
+ }
2566
+ parse(tokens) {
2567
+ const [tree, _] = this.parseGroup(tokens, 0, tokens.length);
2568
+ return tree;
2569
+ }
2570
+ parseGroup(tokens, start, end, parentheses = false) {
2571
+ const results = [];
2572
+ let pos = start;
2573
+ while (pos < end) {
2574
+ const [res, newPos] = this.parseNextExpr(tokens, pos);
2575
+ pos = newPos;
2576
+ if (res.type === "whitespace") {
2577
+ if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
2578
+ continue;
2579
+ }
2580
+ if (!this.newline_sensitive && res.content === `
2581
+ `) {
2582
+ continue;
2583
+ }
2584
+ }
2585
+ results.push(res);
2586
+ }
2587
+ let node;
2588
+ if (parentheses) {
2589
+ node = process_operators(results, true);
2590
+ } else {
2591
+ if (results.length === 0) {
2592
+ node = TYPST_EMPTY_NODE;
2593
+ } else if (results.length === 1) {
2594
+ node = results[0];
2595
+ } else {
2596
+ node = process_operators(results);
2597
+ }
2598
+ }
2599
+ return [node, end + 1];
2600
+ }
2601
+ parseNextExpr(tokens, start) {
2602
+ let [base, pos] = this.parseNextExprWithoutSupSub(tokens, start);
2603
+ let sub = null;
2604
+ let sup = null;
2605
+ const num_base_prime = eat_primes2(tokens, pos);
2606
+ if (num_base_prime > 0) {
2607
+ base = new TypstNode("group", "", [base].concat(primes(num_base_prime)));
2608
+ pos += num_base_prime;
2609
+ }
2610
+ if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL2)) {
2611
+ [sub, pos] = this.parseSupOrSub(tokens, pos + 1);
2612
+ if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL2)) {
2613
+ [sup, pos] = this.parseSupOrSub(tokens, pos + 1);
2614
+ }
2615
+ } else if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL2)) {
2616
+ [sup, pos] = this.parseSupOrSub(tokens, pos + 1);
2617
+ if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL2)) {
2618
+ [sub, pos] = this.parseSupOrSub(tokens, pos + 1);
2619
+ }
2620
+ }
2621
+ if (sub !== null || sup !== null) {
2622
+ const res = { base };
2623
+ if (sub) {
2624
+ res.sub = sub;
2625
+ }
2626
+ if (sup) {
2627
+ res.sup = sup;
2628
+ }
2629
+ return [new TypstNode("supsub", "", [], res), pos];
2630
+ } else {
2631
+ return [base, pos];
2632
+ }
2633
+ }
2634
+ parseSupOrSub(tokens, start) {
2635
+ let node;
2636
+ let end;
2637
+ if (tokens[start].eq(LEFT_PARENTHESES)) {
2638
+ const pos_closing = find_closing_match2(tokens, start);
2639
+ [node, end] = this.parseGroup(tokens, start + 1, pos_closing);
2640
+ } else {
2641
+ [node, end] = this.parseNextExprWithoutSupSub(tokens, start);
2642
+ }
2643
+ const num_prime = eat_primes2(tokens, end);
2644
+ if (num_prime > 0) {
2645
+ node = new TypstNode("group", "", [node].concat(primes(num_prime)));
2646
+ end += num_prime;
2647
+ }
2648
+ return [node, end];
2649
+ }
2650
+ parseNextExprWithoutSupSub(tokens, start) {
2651
+ const firstToken = tokens[start];
2652
+ const node = firstToken.toNode();
2653
+ if (firstToken.eq(LEFT_PARENTHESES)) {
2654
+ const pos_closing = find_closing_match2(tokens, start);
2655
+ return this.parseGroup(tokens, start + 1, pos_closing, true);
2656
+ }
2657
+ if (firstToken.type === 1 /* ELEMENT */ && !isalpha(firstToken.value[0])) {
2658
+ return [node, start + 1];
2659
+ }
2660
+ if ([1 /* ELEMENT */, 0 /* SYMBOL */].includes(firstToken.type)) {
2661
+ if (start + 1 < tokens.length && tokens[start + 1].eq(LEFT_PARENTHESES)) {
2662
+ if (firstToken.value === "mat") {
2663
+ const [matrix, newPos2] = this.parseGroupsOfArguments(tokens, start + 1);
2664
+ const mat = new TypstNode("matrix", "", [], matrix);
2665
+ return [mat, newPos2];
2666
+ }
2667
+ const [args, newPos] = this.parseArguments(tokens, start + 1);
2668
+ const func_call = new TypstNode("funcCall", firstToken.value);
2669
+ func_call.args = args;
2670
+ return [func_call, newPos];
2671
+ }
2672
+ }
2673
+ return [node, start + 1];
2674
+ }
2675
+ parseArguments(tokens, start) {
2676
+ const end = find_closing_match2(tokens, start);
2677
+ return [this.parseCommaSeparatedArguments(tokens, start + 1, end), end + 1];
2678
+ }
2679
+ parseGroupsOfArguments(tokens, start) {
2680
+ const end = find_closing_match2(tokens, start);
2681
+ const matrix = [];
2682
+ let pos = start + 1;
2683
+ while (pos < end) {
2684
+ while (pos < end) {
2685
+ let next_stop = array_find(tokens, SEMICOLON, pos);
2686
+ if (next_stop === -1) {
2687
+ next_stop = end;
2688
+ }
2689
+ const row = this.parseCommaSeparatedArguments(tokens, pos, next_stop);
2690
+ matrix.push(row);
2691
+ pos = next_stop + 1;
2692
+ }
2693
+ }
2694
+ return [matrix, end + 1];
2695
+ }
2696
+ parseCommaSeparatedArguments(tokens, start, end) {
2697
+ const args = [];
2698
+ let pos = start;
2699
+ while (pos < end) {
2700
+ let arg = new TypstNode("group", "", []);
2701
+ while (pos < end) {
2702
+ if (tokens[pos].eq(COMMA)) {
2703
+ pos += 1;
2704
+ break;
2705
+ } else if (tokens[pos].eq(SINGLE_SPACE)) {
2706
+ pos += 1;
2707
+ continue;
2708
+ }
2709
+ const [argItem, newPos] = this.parseNextExpr(tokens, pos);
2710
+ pos = newPos;
2711
+ arg.args.push(argItem);
2712
+ }
2713
+ if (arg.args.length === 0) {
2714
+ arg = TYPST_EMPTY_NODE;
2715
+ } else if (arg.args.length === 1) {
2716
+ arg = arg.args[0];
2717
+ }
2718
+ args.push(arg);
2719
+ }
2720
+ return args;
2721
+ }
2722
+ }
2723
+ function parseTypst(typst) {
2724
+ const parser = new TypstParser;
2725
+ let tokens = tokenize_typst(typst);
2726
+ return parser.parse(tokens);
2727
+ }
2728
+
2729
+ // src/tex-writer.ts
2730
+ var TYPST_UNARY_FUNCTIONS = [
2731
+ "sqrt",
2732
+ "bold",
2733
+ "arrow",
2734
+ "upright",
2735
+ "lr",
2736
+ "op",
2737
+ "macron",
2738
+ "dot",
2739
+ "dot.double",
2740
+ "hat",
2741
+ "tilde",
2742
+ "overline",
2743
+ "underline",
2744
+ "bb",
2745
+ "cal",
2746
+ "frak"
2747
+ ];
2748
+ var TYPST_BINARY_FUNCTIONS = [
2749
+ "frac",
2750
+ "root",
2751
+ "overbrace",
2752
+ "underbrace"
2753
+ ];
2754
+ function apply_escape_if_needed2(c) {
2755
+ if (["{", "}", "%"].includes(c)) {
2756
+ return "\\" + c;
2757
+ }
2758
+ return c;
2759
+ }
2760
+
2761
+ class TexWriter {
2762
+ buffer = "";
2763
+ queue = [];
2764
+ writeBuffer(token) {
2765
+ const str = token.toString();
2766
+ let no_need_space = false;
2767
+ if (token.type === 4 /* SPACE */) {
2768
+ no_need_space = true;
2769
+ } else {
2770
+ no_need_space ||= /[{\(\[\|]$/.test(this.buffer);
2771
+ no_need_space ||= /\\\w+$/.test(this.buffer) && str === "[";
2772
+ no_need_space ||= /^[\.,;:!\?\(\)\]{}_^]$/.test(str);
2773
+ no_need_space ||= ["\\{", "\\}"].includes(str);
2774
+ no_need_space ||= str === "'";
2775
+ no_need_space ||= this.buffer.endsWith("_") || this.buffer.endsWith("^");
2776
+ no_need_space ||= /\s$/.test(this.buffer);
2777
+ no_need_space ||= /^\s/.test(str);
2778
+ no_need_space ||= this.buffer === "";
2779
+ no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
2780
+ no_need_space ||= this.buffer.endsWith("&") && str === "=";
2781
+ }
2782
+ if (!no_need_space) {
2783
+ this.buffer += " ";
2784
+ }
2785
+ this.buffer += str;
2786
+ }
2787
+ append(node) {
2788
+ const alignment_char = new TexNode("control", "&");
2789
+ const newline_char = new TexNode("control", "\\\\");
2790
+ if (node.type === "ordgroup" && array_includes(node.args, alignment_char)) {
2791
+ const rows = array_split(node.args, newline_char);
2792
+ const data = [];
2793
+ for (const row of rows) {
2794
+ const cells = array_split(row, alignment_char);
2795
+ data.push(cells.map((cell) => new TexNode("ordgroup", "", cell)));
2796
+ }
2797
+ node = new TexNode("beginend", "aligned", [], data);
2798
+ }
2799
+ this.queue = this.queue.concat(node.serialize());
2800
+ }
2801
+ flushQueue() {
2802
+ for (let i = 0;i < this.queue.length; i++) {
2803
+ this.writeBuffer(this.queue[i]);
2804
+ }
2805
+ this.queue = [];
2806
+ }
2807
+ finalize() {
2808
+ this.flushQueue();
2809
+ return this.buffer;
2810
+ }
2811
+ }
2812
+ function convert_typst_node_to_tex(node) {
2813
+ if (node.eq(new TypstNode("symbol", "eq.def"))) {
2814
+ return new TexNode("binaryFunc", "\\overset", [
2815
+ new TexNode("text", "def"),
2816
+ new TexNode("element", "=")
2817
+ ]);
2818
+ }
2819
+ switch (node.type) {
2820
+ case "empty":
2821
+ return new TexNode("empty", "");
2822
+ case "whitespace":
2823
+ return new TexNode("whitespace", node.content);
2824
+ case "atom":
2825
+ if (node.content === ":") {
2826
+ return new TexNode("symbol", "\\colon");
2827
+ }
2828
+ return new TexNode("element", node.content);
2829
+ case "symbol":
2830
+ if (node.content === "comma") {
2831
+ return new TexNode("element", ",");
2832
+ }
2833
+ return new TexNode("symbol", typst_token_to_tex(node.content));
2834
+ case "text":
2835
+ return new TexNode("text", node.content);
2836
+ case "comment":
2837
+ return new TexNode("comment", node.content);
2838
+ case "group": {
2839
+ const args = node.args.map(convert_typst_node_to_tex);
2840
+ if (node.content === "parenthesis") {
2841
+ args.unshift(new TexNode("element", "("));
2842
+ args.push(new TexNode("element", ")"));
2843
+ }
2844
+ return new TexNode("ordgroup", "", args);
2845
+ }
2846
+ case "funcCall": {
2847
+ if (TYPST_UNARY_FUNCTIONS.includes(node.content)) {
2848
+ if (node.content === "lr") {
2849
+ const body = node.args[0];
2850
+ if (body.type === "group") {
2851
+ let left_delim = body.args[0].content;
2852
+ let right_delim = body.args[body.args.length - 1].content;
2853
+ left_delim = apply_escape_if_needed2(left_delim);
2854
+ right_delim = apply_escape_if_needed2(right_delim);
2855
+ return new TexNode("ordgroup", "", [
2856
+ new TexNode("element", "\\left" + left_delim),
2857
+ ...body.args.slice(1, body.args.length - 1).map(convert_typst_node_to_tex),
2858
+ new TexNode("element", "\\right" + right_delim)
2859
+ ]);
2860
+ }
2861
+ }
2862
+ const command = typst_token_to_tex(node.content);
2863
+ return new TexNode("unaryFunc", command, node.args.map(convert_typst_node_to_tex));
2864
+ } else if (TYPST_BINARY_FUNCTIONS.includes(node.content)) {
2865
+ if (node.content === "root") {
2866
+ const [degree, radicand] = node.args;
2867
+ const data = convert_typst_node_to_tex(degree);
2868
+ return new TexNode("unaryFunc", "\\sqrt", [convert_typst_node_to_tex(radicand)], data);
2869
+ }
2870
+ if (node.content === "overbrace" || node.content === "underbrace") {
2871
+ const [body, label] = node.args;
2872
+ const base = new TexNode("unaryFunc", "\\" + node.content, [convert_typst_node_to_tex(body)]);
2873
+ const script = convert_typst_node_to_tex(label);
2874
+ const data = node.content === "overbrace" ? { base, sup: script } : { base, sub: script };
2875
+ return new TexNode("supsub", "", [], data);
2876
+ }
2877
+ const command = typst_token_to_tex(node.content);
2878
+ return new TexNode("binaryFunc", command, node.args.map(convert_typst_node_to_tex));
2879
+ } else {
2880
+ return new TexNode("ordgroup", "", [
2881
+ new TexNode("symbol", typst_token_to_tex(node.content)),
2882
+ new TexNode("element", "("),
2883
+ ...node.args.map(convert_typst_node_to_tex),
2884
+ new TexNode("element", ")")
2885
+ ]);
2886
+ }
2887
+ }
2888
+ case "supsub": {
2889
+ const { base, sup, sub } = node.data;
2890
+ const base_tex = convert_typst_node_to_tex(base);
2891
+ let sup_tex;
2892
+ let sub_tex;
2893
+ if (sup) {
2894
+ sup_tex = convert_typst_node_to_tex(sup);
2895
+ }
2896
+ if (sub) {
2897
+ sub_tex = convert_typst_node_to_tex(sub);
2898
+ }
2899
+ const res = new TexNode("supsub", "", [], {
2900
+ base: base_tex,
2901
+ sup: sup_tex,
2902
+ sub: sub_tex
2903
+ });
2904
+ return res;
2905
+ }
2906
+ case "matrix": {
2907
+ const typst_data = node.data;
2908
+ const tex_data = typst_data.map((row) => row.map(convert_typst_node_to_tex));
2909
+ const matrix = new TexNode("beginend", "matrix", [], tex_data);
2910
+ return new TexNode("ordgroup", "", [
2911
+ new TexNode("element", "\\left("),
2912
+ matrix,
2913
+ new TexNode("element", "\\right)")
2914
+ ]);
2915
+ }
2916
+ case "control": {
2917
+ switch (node.content) {
2918
+ case "\\":
2919
+ return new TexNode("control", "\\\\");
2920
+ case "&":
2921
+ return new TexNode("control", "&");
2922
+ default:
2923
+ throw new Error("[convert_typst_node_to_tex] Unimplemented control: " + node.content);
2924
+ }
2925
+ }
2926
+ case "fraction": {
2927
+ const [numerator, denominator] = node.args;
2928
+ const num_tex = convert_typst_node_to_tex(numerator);
2929
+ const den_tex = convert_typst_node_to_tex(denominator);
2930
+ return new TexNode("binaryFunc", "\\frac", [num_tex, den_tex]);
2931
+ }
2932
+ default:
2933
+ throw new Error("[convert_typst_node_to_tex] Unimplemented type: " + node.type);
2934
+ }
2935
+ }
2936
+ function typst_token_to_tex(token) {
2937
+ if (/^[a-zA-Z0-9]$/.test(token)) {
2938
+ return token;
2939
+ } else if (token === "thin") {
2940
+ return "\\,";
2941
+ } else if (reverseSymbolMap.has(token)) {
2942
+ return "\\" + reverseSymbolMap.get(token);
2943
+ }
2944
+ return "\\" + token;
2945
+ }
2946
+
2162
2947
  // src/index.ts
2163
2948
  function tex2typst(tex, options) {
2164
2949
  const opt = {
@@ -2180,11 +2965,19 @@ function tex2typst(tex, options) {
2180
2965
  }
2181
2966
  const texTree = parseTex(tex, opt.customTexMacros);
2182
2967
  const typstTree = convertTree(texTree);
2183
- const writer2 = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
2184
- writer2.serialize(typstTree);
2185
- return writer2.finalize();
2968
+ const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
2969
+ writer.serialize(typstTree);
2970
+ return writer.finalize();
2971
+ }
2972
+ function typst2tex(typst) {
2973
+ const typstTree = parseTypst(typst);
2974
+ const texTree = convert_typst_node_to_tex(typstTree);
2975
+ const writer = new TexWriter;
2976
+ writer.append(texTree);
2977
+ return writer.finalize();
2186
2978
  }
2187
2979
  export {
2980
+ typst2tex,
2188
2981
  tex2typst,
2189
2982
  symbolMap
2190
2983
  };