tex2typst 0.3.0-beta-7 → 0.3.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/README.md +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +346 -178
- package/dist/tex-parser.d.ts +1 -0
- package/dist/tex2typst.min.js +20 -19
- package/dist/types.d.ts +6 -4
- package/package.json +5 -5
- package/src/convert.ts +45 -5
- package/src/index.ts +1 -1
- package/src/tex-parser.ts +33 -44
- package/src/tex-writer.ts +0 -1
- package/src/types.ts +8 -2
- package/src/typst-parser.ts +2 -3
- package/src/typst-writer.ts +23 -3
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/map.ts
|
|
2
|
-
var symbolMap = new Map([
|
|
2
|
+
var symbolMap = /* @__PURE__ */ new Map([
|
|
3
3
|
["nonumber", ""],
|
|
4
4
|
["vec", "arrow"],
|
|
5
5
|
["neq", "eq.not"],
|
|
@@ -10,13 +10,19 @@ var symbolMap = new Map([
|
|
|
10
10
|
["vdots", "dots.v"],
|
|
11
11
|
["ddots", "dots.down"],
|
|
12
12
|
["widehat", "hat"],
|
|
13
|
+
// Ideally, the result of \widehat should be longer than \hat. But it is not implemented now.
|
|
13
14
|
["widetilde", "tilde"],
|
|
15
|
+
// Ideally, the result of \widetilde should be longer than \tilde. But it is not implemented now.
|
|
14
16
|
["quad", "quad"],
|
|
15
17
|
["qquad", "wide"],
|
|
16
18
|
["overbrace", "overbrace"],
|
|
19
|
+
// same
|
|
17
20
|
["underbrace", "underbrace"],
|
|
21
|
+
// same
|
|
18
22
|
["overline", "overline"],
|
|
23
|
+
// same
|
|
19
24
|
["underline", "underline"],
|
|
25
|
+
// same
|
|
20
26
|
["bar", "macron"],
|
|
21
27
|
["dbinom", "binom"],
|
|
22
28
|
["tbinom", "binom"],
|
|
@@ -33,18 +39,25 @@ var symbolMap = new Map([
|
|
|
33
39
|
["mathsf", "sans"],
|
|
34
40
|
["mathtt", "mono"],
|
|
35
41
|
["rm", "upright"],
|
|
42
|
+
// TODO: \pmb need special logic to handle but it is not implemented now. See the commented test case.
|
|
36
43
|
["pmb", "bold"],
|
|
44
|
+
/* variants of plus,minus,times,divide */
|
|
37
45
|
["pm", "plus.minus"],
|
|
38
46
|
["mp", "minus.plus"],
|
|
39
47
|
["boxplus", "plus.square"],
|
|
40
48
|
["otimes", "times.circle"],
|
|
41
49
|
["boxtimes", "times.square"],
|
|
50
|
+
/* wave */
|
|
51
|
+
// tex: \sim \approx \cong \simeq \asymp \equiv \propto
|
|
52
|
+
// typst: tilde.op approx tilde.equiv tilde.eq ≍ equiv prop
|
|
42
53
|
["approx", "approx"],
|
|
43
54
|
["cong", "tilde.equiv"],
|
|
44
55
|
["simeq", "tilde.eq"],
|
|
45
|
-
["asymp", "
|
|
56
|
+
["asymp", "\u224D"],
|
|
57
|
+
// just use the unicode character :-)
|
|
46
58
|
["equiv", "equiv"],
|
|
47
59
|
["propto", "prop"],
|
|
60
|
+
/* arrows */
|
|
48
61
|
["gets", "arrow.l"],
|
|
49
62
|
["hookleftarrow", "arrow.l.hook"],
|
|
50
63
|
["leftharpoonup", "harpoon.lt"],
|
|
@@ -56,6 +69,7 @@ var symbolMap = new Map([
|
|
|
56
69
|
["Longleftarrow", "arrow.l.double.long"],
|
|
57
70
|
["Longrightarrow", "arrow.r.double.long"],
|
|
58
71
|
["Longleftrightarrow", "arrow.l.r.double.long"],
|
|
72
|
+
// ['longmapsto', 'arrow.r.bar'],
|
|
59
73
|
["hookrightarrow", "arrow.r.hook"],
|
|
60
74
|
["rightharpoonup", "harpoon.rt"],
|
|
61
75
|
["rightharpoondown", "harpoon.rb"],
|
|
@@ -94,26 +108,34 @@ var symbolMap = new Map([
|
|
|
94
108
|
["Theta", "Theta"],
|
|
95
109
|
["aleph", "alef"],
|
|
96
110
|
["alpha", "alpha"],
|
|
111
|
+
// ['amalg', 'product.co'],
|
|
97
112
|
["angle", "angle"],
|
|
98
113
|
["approx", "approx"],
|
|
99
114
|
["approxeq", "approx.eq"],
|
|
115
|
+
// ['ast', 'ast'],
|
|
100
116
|
["beta", "beta"],
|
|
101
117
|
["bigcap", "sect.big"],
|
|
102
118
|
["bigcirc", "circle.big"],
|
|
103
119
|
["bigcup", "union.big"],
|
|
104
120
|
["bigodot", "dot.circle.big"],
|
|
121
|
+
// ['bigoplus', 'xor.big'], // or "plus.circle.big"
|
|
105
122
|
["bigotimes", "times.circle.big"],
|
|
106
123
|
["bigsqcup", "union.sq.big"],
|
|
124
|
+
// ['bigtriangledown', 'triangle.b'],
|
|
125
|
+
// ['bigtriangleup', 'triangle.t'],
|
|
107
126
|
["biguplus", "union.plus.big"],
|
|
108
127
|
["bigvee", "or.big"],
|
|
109
128
|
["bigwedge", "and.big"],
|
|
129
|
+
// ['bowtie', 'join'],
|
|
110
130
|
["bullet", "bullet"],
|
|
111
131
|
["cap", "sect"],
|
|
112
132
|
["cdot", "dot.op"],
|
|
133
|
+
// 'dot.op' or 'dot.c'
|
|
113
134
|
["cdots", "dots.c"],
|
|
114
135
|
["checkmark", "checkmark"],
|
|
115
136
|
["chi", "chi"],
|
|
116
137
|
["circ", "circle.small"],
|
|
138
|
+
// 'circle.small' or 'compose'
|
|
117
139
|
["colon", "colon"],
|
|
118
140
|
["cong", "tilde.equiv"],
|
|
119
141
|
["coprod", "product.co"],
|
|
@@ -138,6 +160,7 @@ var symbolMap = new Map([
|
|
|
138
160
|
["eta", "eta"],
|
|
139
161
|
["exists", "exists"],
|
|
140
162
|
["forall", "forall"],
|
|
163
|
+
// ['frown', 'paren.t'],
|
|
141
164
|
["gamma", "gamma"],
|
|
142
165
|
["ge", "gt.eq"],
|
|
143
166
|
["geq", "gt.eq"],
|
|
@@ -152,6 +175,7 @@ var symbolMap = new Map([
|
|
|
152
175
|
["infty", "infinity"],
|
|
153
176
|
["int", "integral"],
|
|
154
177
|
["intercal", "top"],
|
|
178
|
+
// 'top' or 'tack.b'
|
|
155
179
|
["iota", "iota"],
|
|
156
180
|
["jmath", "dotless.j"],
|
|
157
181
|
["kappa", "kappa"],
|
|
@@ -170,13 +194,17 @@ var symbolMap = new Map([
|
|
|
170
194
|
["leqslant", "lt.eq.slant"],
|
|
171
195
|
["lhd", "triangle.l"],
|
|
172
196
|
["ll", "lt.double"],
|
|
197
|
+
// ['longmapsto', 'arrow.bar.long'],
|
|
198
|
+
// ['longrightarrow', 'arrow.long'],
|
|
173
199
|
["lor", "or"],
|
|
174
200
|
["ltimes", "times.l"],
|
|
201
|
+
// ['mapsto', 'arrow.bar'],
|
|
175
202
|
["measuredangle", "angle.arc"],
|
|
176
203
|
["mid", "divides"],
|
|
177
204
|
["models", "models"],
|
|
178
205
|
["mp", "minus.plus"],
|
|
179
206
|
["mu", "mu"],
|
|
207
|
+
// ['nRightarrow', 'arrow.double.not'],
|
|
180
208
|
["nabla", "nabla"],
|
|
181
209
|
["ncong", "tilde.equiv.not"],
|
|
182
210
|
["ne", "eq.not"],
|
|
@@ -190,6 +218,7 @@ var symbolMap = new Map([
|
|
|
190
218
|
["ngeq", "gt.eq.not"],
|
|
191
219
|
["nmid", "divides.not"],
|
|
192
220
|
["notin", "in.not"],
|
|
221
|
+
// ['nrightarrow', 'arrow.not'],
|
|
193
222
|
["nsim", "tilde.not"],
|
|
194
223
|
["nsubseteq", "subset.eq.not"],
|
|
195
224
|
["nu", "nu"],
|
|
@@ -201,7 +230,9 @@ var symbolMap = new Map([
|
|
|
201
230
|
["oiint", "integral.surf"],
|
|
202
231
|
["oiiint", "integral.vol"],
|
|
203
232
|
["omega", "omega"],
|
|
233
|
+
// ['omicron', 'omicron'],
|
|
204
234
|
["ominus", "minus.circle"],
|
|
235
|
+
// ['oplus', 'xor'], // or 'plus.circle'
|
|
205
236
|
["otimes", "times.circle"],
|
|
206
237
|
["parallel", "parallel"],
|
|
207
238
|
["partial", "diff"],
|
|
@@ -230,11 +261,13 @@ var symbolMap = new Map([
|
|
|
230
261
|
["simeq", "tilde.eq"],
|
|
231
262
|
["slash", "slash"],
|
|
232
263
|
["smallsetminus", "without"],
|
|
264
|
+
// ['smile', 'paren.b'],
|
|
233
265
|
["spadesuit", "suit.spade"],
|
|
234
266
|
["sqcap", "sect.sq"],
|
|
235
267
|
["sqcup", "union.sq"],
|
|
236
268
|
["sqsubseteq", "subset.eq.sq"],
|
|
237
269
|
["sqsupseteq", "supset.eq.sq"],
|
|
270
|
+
// ['star', 'star'],
|
|
238
271
|
["subset", "subset"],
|
|
239
272
|
["subseteq", "subset.eq"],
|
|
240
273
|
["subsetneq", "subset.neq"],
|
|
@@ -251,6 +284,9 @@ var symbolMap = new Map([
|
|
|
251
284
|
["to", "arrow.r"],
|
|
252
285
|
["top", "top"],
|
|
253
286
|
["triangle", "triangle.t"],
|
|
287
|
+
// ['triangledown', 'triangle.b.small'],
|
|
288
|
+
// ['triangleleft', 'triangle.l.small'],
|
|
289
|
+
// ['triangleright', 'triangle.r.small'],
|
|
254
290
|
["twoheadrightarrow", "arrow.r.twohead"],
|
|
255
291
|
["uparrow", "arrow.t"],
|
|
256
292
|
["updownarrow", "arrow.t.b"],
|
|
@@ -259,6 +295,7 @@ var symbolMap = new Map([
|
|
|
259
295
|
["upsilon", "upsilon"],
|
|
260
296
|
["varepsilon", "epsilon"],
|
|
261
297
|
["varnothing", "diameter"],
|
|
298
|
+
// empty set
|
|
262
299
|
["varphi", "phi"],
|
|
263
300
|
["varpi", "pi.alt"],
|
|
264
301
|
["varrho", "rho.alt"],
|
|
@@ -272,11 +309,12 @@ var symbolMap = new Map([
|
|
|
272
309
|
["xi", "xi"],
|
|
273
310
|
["yen", "yen"],
|
|
274
311
|
["zeta", "zeta"],
|
|
312
|
+
// extended
|
|
275
313
|
["mathscr", "scr"],
|
|
276
314
|
["LaTeX", "#LaTeX"],
|
|
277
315
|
["TeX", "#TeX"]
|
|
278
316
|
]);
|
|
279
|
-
var map_from_official_docs = new Map([
|
|
317
|
+
var map_from_official_docs = /* @__PURE__ */ new Map([
|
|
280
318
|
["lparen", "paren.l"],
|
|
281
319
|
["lParen", "paren.l.double"],
|
|
282
320
|
["rparen", "paren.r"],
|
|
@@ -287,6 +325,7 @@ var map_from_official_docs = new Map([
|
|
|
287
325
|
["lBrace", "brace.l.double"],
|
|
288
326
|
["rbrace", "brace.r"],
|
|
289
327
|
["rBrace", "brace.r.double"],
|
|
328
|
+
// ['overbrace', 'brace.t'],
|
|
290
329
|
["underbrace", "brace.b"],
|
|
291
330
|
["lbrack", "bracket.l"],
|
|
292
331
|
["lBrack", "bracket.l.double"],
|
|
@@ -367,6 +406,7 @@ var map_from_official_docs = new Map([
|
|
|
367
406
|
["mathquestion", "quest"],
|
|
368
407
|
["Question", "quest.double"],
|
|
369
408
|
["mathoctothorpe", "hash"],
|
|
409
|
+
// ['mathhyphen', 'hyph'],
|
|
370
410
|
["mathpercent", "percent"],
|
|
371
411
|
["mathparagraph", "pilcrow"],
|
|
372
412
|
["mathsection", "section"],
|
|
@@ -435,6 +475,8 @@ var map_from_official_docs = new Map([
|
|
|
435
475
|
["stareq", "eq.star"],
|
|
436
476
|
["circledequal", "eq.circle"],
|
|
437
477
|
["eqcolon", "eq.colon"],
|
|
478
|
+
// \usepackage{mathtools} defines \eqdef
|
|
479
|
+
// https://tex.stackexchange.com/questions/28836/typesetting-the-define-equals-symbol
|
|
438
480
|
["eqdef", "eq.def"],
|
|
439
481
|
["triangleq", "eq.delta"],
|
|
440
482
|
["veeeq", "eq.equi"],
|
|
@@ -936,6 +978,64 @@ var map_from_official_docs = new Map([
|
|
|
936
978
|
["barV", "tack.b.double"],
|
|
937
979
|
["shortdowntack", "tack.b.short"],
|
|
938
980
|
["dashVdash", "tack.l.r"],
|
|
981
|
+
/*
|
|
982
|
+
['mupalpha', 'alpha'],
|
|
983
|
+
['mupbeta', 'beta'],
|
|
984
|
+
['mupchi', 'chi'],
|
|
985
|
+
['mupdelta', 'delta'],
|
|
986
|
+
['mupvarepsilon', 'epsilon'],
|
|
987
|
+
['mupepsilon', 'epsilon.alt'],
|
|
988
|
+
['mupeta', 'eta'],
|
|
989
|
+
['mupgamma', 'gamma'],
|
|
990
|
+
['mupiota', 'iota'],
|
|
991
|
+
['mupkappa', 'kappa'],
|
|
992
|
+
['mupvarkappa', 'kappa.alt'],
|
|
993
|
+
['muplambda', 'lambda'],
|
|
994
|
+
['mupmu', 'mu'],
|
|
995
|
+
['mupnu', 'nu'],
|
|
996
|
+
['mho', 'ohm.inv'],
|
|
997
|
+
['mupomega', 'omega'],
|
|
998
|
+
['mupomicron', 'omicron'],
|
|
999
|
+
['mupvarphi', 'phi'],
|
|
1000
|
+
['mupphi', 'phi.alt'],
|
|
1001
|
+
['muppi', 'pi'],
|
|
1002
|
+
['mupvarpi', 'pi.alt'],
|
|
1003
|
+
['muppsi', 'psi'],
|
|
1004
|
+
['muprho', 'rho'],
|
|
1005
|
+
['mupvarrho', 'rho.alt'],
|
|
1006
|
+
['mupsigma', 'sigma'],
|
|
1007
|
+
['mupvarsigma', 'sigma.alt'],
|
|
1008
|
+
['muptau', 'tau'],
|
|
1009
|
+
['muptheta', 'theta'],
|
|
1010
|
+
['mupvartheta', 'theta.alt'],
|
|
1011
|
+
['mupupsilon', 'upsilon'],
|
|
1012
|
+
['mupxi', 'xi'],
|
|
1013
|
+
['mupzeta', 'zeta'],
|
|
1014
|
+
['mupAlpha', 'Alpha'],
|
|
1015
|
+
['mupBeta', 'Beta'],
|
|
1016
|
+
['mupChi', 'Chi'],
|
|
1017
|
+
['mupDelta', 'Delta'],
|
|
1018
|
+
['mupEpsilon', 'Epsilon'],
|
|
1019
|
+
['mupEta', 'Eta'],
|
|
1020
|
+
['mupGamma', 'Gamma'],
|
|
1021
|
+
['mupIota', 'Iota'],
|
|
1022
|
+
['mupKappa', 'Kappa'],
|
|
1023
|
+
['mupLambda', 'Lambda'],
|
|
1024
|
+
['mupMu', 'Mu'],
|
|
1025
|
+
['mupNu', 'Nu'],
|
|
1026
|
+
['mupOmega', 'Omega'],
|
|
1027
|
+
['mupOmicron', 'Omicron'],
|
|
1028
|
+
['mupPhi', 'Phi'],
|
|
1029
|
+
['mupPi', 'Pi'],
|
|
1030
|
+
['mupPsi', 'Psi'],
|
|
1031
|
+
['mupRho', 'Rho'],
|
|
1032
|
+
['mupSigma', 'Sigma'],
|
|
1033
|
+
['mupTau', 'Tau'],
|
|
1034
|
+
['mupTheta', 'Theta'],
|
|
1035
|
+
['mupUpsilon', 'Upsilon'],
|
|
1036
|
+
['mupXi', 'Xi'],
|
|
1037
|
+
['mupZeta', 'Zeta'],
|
|
1038
|
+
*/
|
|
939
1039
|
["BbbA", "AA"],
|
|
940
1040
|
["BbbB", "BB"],
|
|
941
1041
|
["BbbC", "CC"],
|
|
@@ -976,12 +1076,12 @@ for (const [key, value] of map_from_official_docs) {
|
|
|
976
1076
|
symbolMap.set(key, value);
|
|
977
1077
|
}
|
|
978
1078
|
}
|
|
979
|
-
var reverseSymbolMap = new Map;
|
|
1079
|
+
var reverseSymbolMap = /* @__PURE__ */ new Map();
|
|
980
1080
|
for (const [key, value] of Array.from(symbolMap.entries()).reverse()) {
|
|
981
1081
|
reverseSymbolMap.set(value, key);
|
|
982
1082
|
}
|
|
983
1083
|
reverseSymbolMap.set("dif", "mathrm{d}");
|
|
984
|
-
var typst_to_tex_map = new Map([
|
|
1084
|
+
var typst_to_tex_map = /* @__PURE__ */ new Map([
|
|
985
1085
|
["top", "top"],
|
|
986
1086
|
["frac", "frac"],
|
|
987
1087
|
["tilde", "tilde"],
|
|
@@ -996,7 +1096,7 @@ for (const [key, value] of typst_to_tex_map) {
|
|
|
996
1096
|
|
|
997
1097
|
// src/generic.ts
|
|
998
1098
|
function array_find(array, item, start = 0) {
|
|
999
|
-
for (let i = start;i < array.length; i++) {
|
|
1099
|
+
for (let i = start; i < array.length; i++) {
|
|
1000
1100
|
if (array[i].eq(item)) {
|
|
1001
1101
|
return i;
|
|
1002
1102
|
}
|
|
@@ -1027,9 +1127,7 @@ function array_split(array, sep) {
|
|
|
1027
1127
|
}
|
|
1028
1128
|
|
|
1029
1129
|
// src/types.ts
|
|
1030
|
-
|
|
1031
|
-
type;
|
|
1032
|
-
value;
|
|
1130
|
+
var TexToken = class {
|
|
1033
1131
|
constructor(type, value) {
|
|
1034
1132
|
this.type = type;
|
|
1035
1133
|
this.value = value;
|
|
@@ -1047,25 +1145,21 @@ class TexToken {
|
|
|
1047
1145
|
return this.value;
|
|
1048
1146
|
}
|
|
1049
1147
|
}
|
|
1050
|
-
}
|
|
1148
|
+
};
|
|
1051
1149
|
function apply_escape_if_needed(c) {
|
|
1052
1150
|
if (["{", "}", "%"].includes(c)) {
|
|
1053
1151
|
return "\\" + c;
|
|
1054
1152
|
}
|
|
1055
1153
|
return c;
|
|
1056
1154
|
}
|
|
1057
|
-
|
|
1058
|
-
class TexNode {
|
|
1059
|
-
type;
|
|
1060
|
-
content;
|
|
1061
|
-
args;
|
|
1062
|
-
data;
|
|
1155
|
+
var TexNode = class {
|
|
1063
1156
|
constructor(type, content, args, data) {
|
|
1064
1157
|
this.type = type;
|
|
1065
1158
|
this.content = content;
|
|
1066
1159
|
this.args = args;
|
|
1067
1160
|
this.data = data;
|
|
1068
1161
|
}
|
|
1162
|
+
// Note that this is only shallow equality.
|
|
1069
1163
|
eq(other) {
|
|
1070
1164
|
return this.type === other.type && this.content === other.content;
|
|
1071
1165
|
}
|
|
@@ -1143,7 +1237,7 @@ class TexNode {
|
|
|
1143
1237
|
return tokens;
|
|
1144
1238
|
}
|
|
1145
1239
|
case "supsub": {
|
|
1146
|
-
let
|
|
1240
|
+
let should_wrap_in_braces2 = function(node) {
|
|
1147
1241
|
if (node.type === "ordgroup" || node.type === "supsub" || node.type === "empty") {
|
|
1148
1242
|
return true;
|
|
1149
1243
|
} else if (node.type === "element" && /\d+(\.\d+)?/.test(node.content) && node.content.length > 1) {
|
|
@@ -1152,12 +1246,13 @@ class TexNode {
|
|
|
1152
1246
|
return false;
|
|
1153
1247
|
}
|
|
1154
1248
|
};
|
|
1249
|
+
var should_wrap_in_braces = should_wrap_in_braces2;
|
|
1155
1250
|
let tokens = [];
|
|
1156
1251
|
const { base, sup, sub } = this.data;
|
|
1157
1252
|
tokens = tokens.concat(base.serialize());
|
|
1158
1253
|
if (sub) {
|
|
1159
1254
|
tokens.push(new TexToken(6 /* CONTROL */, "_"));
|
|
1160
|
-
if (
|
|
1255
|
+
if (should_wrap_in_braces2(sub)) {
|
|
1161
1256
|
tokens.push(new TexToken(0 /* ELEMENT */, "{"));
|
|
1162
1257
|
tokens = tokens.concat(sub.serialize());
|
|
1163
1258
|
tokens.push(new TexToken(0 /* ELEMENT */, "}"));
|
|
@@ -1167,7 +1262,7 @@ class TexNode {
|
|
|
1167
1262
|
}
|
|
1168
1263
|
if (sup) {
|
|
1169
1264
|
tokens.push(new TexToken(6 /* CONTROL */, "^"));
|
|
1170
|
-
if (
|
|
1265
|
+
if (should_wrap_in_braces2(sup)) {
|
|
1171
1266
|
tokens.push(new TexToken(0 /* ELEMENT */, "{"));
|
|
1172
1267
|
tokens = tokens.concat(sup.serialize());
|
|
1173
1268
|
tokens.push(new TexToken(0 /* ELEMENT */, "}"));
|
|
@@ -1184,11 +1279,10 @@ class TexNode {
|
|
|
1184
1279
|
let tokens = [];
|
|
1185
1280
|
const matrix = this.data;
|
|
1186
1281
|
tokens.push(new TexToken(1 /* COMMAND */, `\\begin{${this.content}}`));
|
|
1187
|
-
tokens.push(new TexToken(5 /* NEWLINE */,
|
|
1188
|
-
|
|
1189
|
-
for (let i = 0;i < matrix.length; i++) {
|
|
1282
|
+
tokens.push(new TexToken(5 /* NEWLINE */, "\n"));
|
|
1283
|
+
for (let i = 0; i < matrix.length; i++) {
|
|
1190
1284
|
const row = matrix[i];
|
|
1191
|
-
for (let j = 0;j < row.length; j++) {
|
|
1285
|
+
for (let j = 0; j < row.length; j++) {
|
|
1192
1286
|
const cell = row[j];
|
|
1193
1287
|
tokens = tokens.concat(cell.serialize());
|
|
1194
1288
|
if (j !== row.length - 1) {
|
|
@@ -1199,8 +1293,7 @@ class TexNode {
|
|
|
1199
1293
|
tokens.push(new TexToken(6 /* CONTROL */, "\\\\"));
|
|
1200
1294
|
}
|
|
1201
1295
|
}
|
|
1202
|
-
tokens.push(new TexToken(5 /* NEWLINE */,
|
|
1203
|
-
`));
|
|
1296
|
+
tokens.push(new TexToken(5 /* NEWLINE */, "\n"));
|
|
1204
1297
|
tokens.push(new TexToken(1 /* COMMAND */, `\\end{${this.content}}`));
|
|
1205
1298
|
return tokens;
|
|
1206
1299
|
}
|
|
@@ -1208,6 +1301,8 @@ class TexNode {
|
|
|
1208
1301
|
throw new Error("[TexNode.serialize] Unimplemented type: " + this.type);
|
|
1209
1302
|
}
|
|
1210
1303
|
}
|
|
1304
|
+
// whether the node is over high so that if it's wrapped in braces, \left and \right should be used.
|
|
1305
|
+
// e.g. \frac{1}{2} is over high, "2" is not.
|
|
1211
1306
|
isOverHigh() {
|
|
1212
1307
|
switch (this.type) {
|
|
1213
1308
|
case "element":
|
|
@@ -1220,6 +1315,7 @@ class TexNode {
|
|
|
1220
1315
|
if (this.content === "\\frac") {
|
|
1221
1316
|
return true;
|
|
1222
1317
|
}
|
|
1318
|
+
// fall through
|
|
1223
1319
|
case "unaryFunc":
|
|
1224
1320
|
case "ordgroup":
|
|
1225
1321
|
return this.args.some((n) => n.isOverHigh());
|
|
@@ -1232,10 +1328,8 @@ class TexNode {
|
|
|
1232
1328
|
return false;
|
|
1233
1329
|
}
|
|
1234
1330
|
}
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
type;
|
|
1238
|
-
value;
|
|
1331
|
+
};
|
|
1332
|
+
var TypstToken = class {
|
|
1239
1333
|
constructor(type, content) {
|
|
1240
1334
|
this.type = type;
|
|
1241
1335
|
this.value = content;
|
|
@@ -1288,14 +1382,10 @@ class TypstToken {
|
|
|
1288
1382
|
return this.value;
|
|
1289
1383
|
}
|
|
1290
1384
|
}
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
content;
|
|
1296
|
-
args;
|
|
1297
|
-
data;
|
|
1298
|
-
options;
|
|
1385
|
+
};
|
|
1386
|
+
var TYPST_NONE = null;
|
|
1387
|
+
var TYPST_TRUE = true;
|
|
1388
|
+
var TypstNode = class {
|
|
1299
1389
|
constructor(type, content, args, data) {
|
|
1300
1390
|
this.type = type;
|
|
1301
1391
|
this.content = content;
|
|
@@ -1305,10 +1395,11 @@ class TypstNode {
|
|
|
1305
1395
|
setOptions(options) {
|
|
1306
1396
|
this.options = options;
|
|
1307
1397
|
}
|
|
1398
|
+
// Note that this is only shallow equality.
|
|
1308
1399
|
eq(other) {
|
|
1309
1400
|
return this.type === other.type && this.content === other.content;
|
|
1310
1401
|
}
|
|
1311
|
-
}
|
|
1402
|
+
};
|
|
1312
1403
|
|
|
1313
1404
|
// src/util.ts
|
|
1314
1405
|
function isalpha(char) {
|
|
@@ -1465,8 +1556,7 @@ function tokenize(latex) {
|
|
|
1465
1556
|
switch (firstChar) {
|
|
1466
1557
|
case "%": {
|
|
1467
1558
|
let newPos = pos + 1;
|
|
1468
|
-
while (newPos < latex.length && latex[newPos] !==
|
|
1469
|
-
`) {
|
|
1559
|
+
while (newPos < latex.length && latex[newPos] !== "\n") {
|
|
1470
1560
|
newPos += 1;
|
|
1471
1561
|
}
|
|
1472
1562
|
token = new TexToken(3 /* COMMENT */, latex.slice(pos + 1, newPos));
|
|
@@ -1481,20 +1571,16 @@ function tokenize(latex) {
|
|
|
1481
1571
|
token = new TexToken(6 /* CONTROL */, firstChar);
|
|
1482
1572
|
pos++;
|
|
1483
1573
|
break;
|
|
1484
|
-
case
|
|
1485
|
-
`:
|
|
1574
|
+
case "\n":
|
|
1486
1575
|
token = new TexToken(5 /* NEWLINE */, firstChar);
|
|
1487
1576
|
pos++;
|
|
1488
1577
|
break;
|
|
1489
1578
|
case "\r": {
|
|
1490
|
-
if (pos + 1 < latex.length && latex[pos + 1] ===
|
|
1491
|
-
|
|
1492
|
-
token = new TexToken(5 /* NEWLINE */, `
|
|
1493
|
-
`);
|
|
1579
|
+
if (pos + 1 < latex.length && latex[pos + 1] === "\n") {
|
|
1580
|
+
token = new TexToken(5 /* NEWLINE */, "\n");
|
|
1494
1581
|
pos += 2;
|
|
1495
1582
|
} else {
|
|
1496
|
-
token = new TexToken(5 /* NEWLINE */,
|
|
1497
|
-
`);
|
|
1583
|
+
token = new TexToken(5 /* NEWLINE */, "\n");
|
|
1498
1584
|
pos++;
|
|
1499
1585
|
}
|
|
1500
1586
|
break;
|
|
@@ -1561,61 +1647,51 @@ function tokenize(latex) {
|
|
|
1561
1647
|
}
|
|
1562
1648
|
return tokens;
|
|
1563
1649
|
}
|
|
1564
|
-
|
|
1565
|
-
class LatexParserError extends Error {
|
|
1650
|
+
var LatexParserError = class extends Error {
|
|
1566
1651
|
constructor(message) {
|
|
1567
1652
|
super(message);
|
|
1568
1653
|
this.name = "LatexParserError";
|
|
1569
1654
|
}
|
|
1570
|
-
}
|
|
1655
|
+
};
|
|
1571
1656
|
var SUB_SYMBOL = new TexToken(6 /* CONTROL */, "_");
|
|
1572
1657
|
var SUP_SYMBOL = new TexToken(6 /* CONTROL */, "^");
|
|
1573
|
-
|
|
1574
|
-
class LatexParser {
|
|
1575
|
-
space_sensitive;
|
|
1576
|
-
newline_sensitive;
|
|
1658
|
+
var LatexParser = class {
|
|
1577
1659
|
constructor(space_sensitive = false, newline_sensitive = true) {
|
|
1578
1660
|
this.space_sensitive = space_sensitive;
|
|
1579
1661
|
this.newline_sensitive = newline_sensitive;
|
|
1580
1662
|
}
|
|
1581
1663
|
parse(tokens) {
|
|
1664
|
+
const [tree, _] = this.parseGroup(tokens, 0, tokens.length);
|
|
1665
|
+
return tree;
|
|
1666
|
+
}
|
|
1667
|
+
parseGroup(tokens, start, end) {
|
|
1582
1668
|
const results = [];
|
|
1583
|
-
let pos =
|
|
1584
|
-
while (pos <
|
|
1585
|
-
const
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
if (res.type === "whitespace") {
|
|
1591
|
-
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1592
|
-
continue;
|
|
1593
|
-
}
|
|
1594
|
-
if (!this.newline_sensitive && res.content === `
|
|
1595
|
-
`) {
|
|
1596
|
-
continue;
|
|
1597
|
-
}
|
|
1669
|
+
let pos = start;
|
|
1670
|
+
while (pos < end) {
|
|
1671
|
+
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
1672
|
+
pos = newPos;
|
|
1673
|
+
if (res.type === "whitespace") {
|
|
1674
|
+
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1675
|
+
continue;
|
|
1598
1676
|
}
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1677
|
+
if (!this.newline_sensitive && res.content === "\n") {
|
|
1678
|
+
continue;
|
|
1601
1679
|
}
|
|
1602
|
-
results2.push(res);
|
|
1603
1680
|
}
|
|
1604
|
-
if (
|
|
1605
|
-
|
|
1606
|
-
} else if (results2.length === 1) {
|
|
1607
|
-
return results2[0];
|
|
1608
|
-
} else {
|
|
1609
|
-
return new TexNode("ordgroup", "", results2);
|
|
1681
|
+
if (res.type === "control" && res.content === "&") {
|
|
1682
|
+
throw new LatexParserError("Unexpected & outside of an alignment");
|
|
1610
1683
|
}
|
|
1684
|
+
results.push(res);
|
|
1611
1685
|
}
|
|
1686
|
+
let node;
|
|
1612
1687
|
if (results.length === 0) {
|
|
1613
|
-
|
|
1688
|
+
node = EMPTY_NODE;
|
|
1614
1689
|
} else if (results.length === 1) {
|
|
1615
|
-
|
|
1690
|
+
node = results[0];
|
|
1616
1691
|
} else {
|
|
1617
|
-
|
|
1692
|
+
node = new TexNode("ordgroup", "", results);
|
|
1618
1693
|
}
|
|
1694
|
+
return [node, end + 1];
|
|
1619
1695
|
}
|
|
1620
1696
|
parseNextExpr(tokens, start) {
|
|
1621
1697
|
let [base, pos] = this.parseNextExprWithoutSupSub(tokens, start);
|
|
@@ -1653,7 +1729,7 @@ class LatexParser {
|
|
|
1653
1729
|
}
|
|
1654
1730
|
if (num_prime > 0) {
|
|
1655
1731
|
res.sup = new TexNode("ordgroup", "", []);
|
|
1656
|
-
for (let i = 0;i < num_prime; i++) {
|
|
1732
|
+
for (let i = 0; i < num_prime; i++) {
|
|
1657
1733
|
res.sup.args.push(new TexNode("element", "'"));
|
|
1658
1734
|
}
|
|
1659
1735
|
if (sup) {
|
|
@@ -1672,8 +1748,7 @@ class LatexParser {
|
|
|
1672
1748
|
}
|
|
1673
1749
|
parseNextExprWithoutSupSub(tokens, start) {
|
|
1674
1750
|
const firstToken = tokens[start];
|
|
1675
|
-
|
|
1676
|
-
switch (tokenType) {
|
|
1751
|
+
switch (firstToken.type) {
|
|
1677
1752
|
case 0 /* ELEMENT */:
|
|
1678
1753
|
return [new TexNode("element", firstToken.value), start + 1];
|
|
1679
1754
|
case 2 /* TEXT */:
|
|
@@ -1699,8 +1774,7 @@ class LatexParser {
|
|
|
1699
1774
|
if (posClosingBracket === -1) {
|
|
1700
1775
|
throw new LatexParserError("Unmatched '{'");
|
|
1701
1776
|
}
|
|
1702
|
-
|
|
1703
|
-
return [this.parse(exprInside), posClosingBracket + 1];
|
|
1777
|
+
return this.parseGroup(tokens, start + 1, posClosingBracket);
|
|
1704
1778
|
case "}":
|
|
1705
1779
|
throw new LatexParserError("Unmatched '}'");
|
|
1706
1780
|
case "\\\\":
|
|
@@ -1743,8 +1817,7 @@ class LatexParser {
|
|
|
1743
1817
|
if (posRightSquareBracket === -1) {
|
|
1744
1818
|
throw new LatexParserError("No matching right square bracket for [");
|
|
1745
1819
|
}
|
|
1746
|
-
const
|
|
1747
|
-
const exponent = this.parse(exprInside);
|
|
1820
|
+
const [exponent, _] = this.parseGroup(tokens, posLeftSquareBracket + 1, posRightSquareBracket);
|
|
1748
1821
|
const [arg12, newPos2] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
|
|
1749
1822
|
return [new TexNode("unaryFunc", command, [arg12], exponent), newPos2];
|
|
1750
1823
|
} else if (command === "\\text") {
|
|
@@ -1797,8 +1870,7 @@ class LatexParser {
|
|
|
1797
1870
|
throw new LatexParserError("Invalid delimiter after \\right");
|
|
1798
1871
|
}
|
|
1799
1872
|
pos++;
|
|
1800
|
-
const
|
|
1801
|
-
const body = this.parse(exprInside);
|
|
1873
|
+
const [body, _] = this.parseGroup(tokens, exprInsideStart, exprInsideEnd);
|
|
1802
1874
|
const args = [
|
|
1803
1875
|
new TexNode("element", leftDelimiter.value),
|
|
1804
1876
|
body,
|
|
@@ -1852,8 +1924,7 @@ class LatexParser {
|
|
|
1852
1924
|
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
1853
1925
|
continue;
|
|
1854
1926
|
}
|
|
1855
|
-
if (!this.newline_sensitive && res.content ===
|
|
1856
|
-
`) {
|
|
1927
|
+
if (!this.newline_sensitive && res.content === "\n") {
|
|
1857
1928
|
continue;
|
|
1858
1929
|
}
|
|
1859
1930
|
}
|
|
@@ -1871,11 +1942,11 @@ class LatexParser {
|
|
|
1871
1942
|
}
|
|
1872
1943
|
return allRows;
|
|
1873
1944
|
}
|
|
1874
|
-
}
|
|
1945
|
+
};
|
|
1875
1946
|
function passIgnoreWhitespaceBeforeScriptMark(tokens) {
|
|
1876
1947
|
const is_script_mark = (token) => token.eq(SUB_SYMBOL) || token.eq(SUP_SYMBOL);
|
|
1877
1948
|
let out_tokens = [];
|
|
1878
|
-
for (let i = 0;i < tokens.length; i++) {
|
|
1949
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1879
1950
|
if (tokens[i].type === 4 /* SPACE */ && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
|
|
1880
1951
|
continue;
|
|
1881
1952
|
}
|
|
@@ -1899,7 +1970,7 @@ function passExpandCustomTexMacros(tokens, customTexMacros) {
|
|
|
1899
1970
|
return out_tokens;
|
|
1900
1971
|
}
|
|
1901
1972
|
function parseTex(tex, customTexMacros) {
|
|
1902
|
-
const parser = new LatexParser;
|
|
1973
|
+
const parser = new LatexParser();
|
|
1903
1974
|
let tokens = tokenize(tex);
|
|
1904
1975
|
tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
|
|
1905
1976
|
tokens = passExpandCustomTexMacros(tokens, customTexMacros);
|
|
@@ -1915,33 +1986,42 @@ var TYPST_INTRINSIC_SYMBOLS = [
|
|
|
1915
1986
|
"Pr",
|
|
1916
1987
|
"sech",
|
|
1917
1988
|
"csch"
|
|
1989
|
+
// 'sgn
|
|
1918
1990
|
];
|
|
1919
1991
|
function is_delimiter(c) {
|
|
1920
|
-
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "
|
|
1992
|
+
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.content);
|
|
1921
1993
|
}
|
|
1922
1994
|
var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
|
|
1923
1995
|
var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
|
|
1924
1996
|
var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
1925
|
-
var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */,
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1997
|
+
var TYPST_NEWLINE = new TypstToken(0 /* SYMBOL */, "\n");
|
|
1998
|
+
function typst_primitive_to_string(value) {
|
|
1999
|
+
switch (typeof value) {
|
|
2000
|
+
case "string":
|
|
2001
|
+
return `"${value}"`;
|
|
2002
|
+
case "number":
|
|
2003
|
+
return value.toString();
|
|
2004
|
+
case "boolean":
|
|
2005
|
+
return value ? "#true" : "#false";
|
|
2006
|
+
default:
|
|
2007
|
+
if (value === null) {
|
|
2008
|
+
return "#none";
|
|
2009
|
+
}
|
|
2010
|
+
throw new TypstWriterError(`Invalid primitive value: ${value}`, value);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
var TypstWriterError = class extends Error {
|
|
1930
2014
|
constructor(message, node) {
|
|
1931
2015
|
super(message);
|
|
1932
2016
|
this.name = "TypstWriterError";
|
|
1933
2017
|
this.node = node;
|
|
1934
2018
|
}
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
class TypstWriter {
|
|
1938
|
-
nonStrict;
|
|
1939
|
-
preferTypstIntrinsic;
|
|
1940
|
-
keepSpaces;
|
|
1941
|
-
buffer = "";
|
|
1942
|
-
queue = [];
|
|
1943
|
-
insideFunctionDepth = 0;
|
|
2019
|
+
};
|
|
2020
|
+
var TypstWriter = class {
|
|
1944
2021
|
constructor(nonStrict, preferTypstIntrinsic, keepSpaces) {
|
|
2022
|
+
this.buffer = "";
|
|
2023
|
+
this.queue = [];
|
|
2024
|
+
this.insideFunctionDepth = 0;
|
|
1945
2025
|
this.nonStrict = nonStrict;
|
|
1946
2026
|
this.preferTypstIntrinsic = preferTypstIntrinsic;
|
|
1947
2027
|
this.keepSpaces = keepSpaces;
|
|
@@ -1959,8 +2039,7 @@ class TypstWriter {
|
|
|
1959
2039
|
no_need_space ||= str === "'";
|
|
1960
2040
|
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1961
2041
|
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
1962
|
-
no_need_space ||= str.startsWith(
|
|
1963
|
-
`);
|
|
2042
|
+
no_need_space ||= str.startsWith("\n");
|
|
1964
2043
|
no_need_space ||= this.buffer === "";
|
|
1965
2044
|
no_need_space ||= /^\s/.test(str);
|
|
1966
2045
|
no_need_space ||= this.buffer.endsWith("&") && str === "=";
|
|
@@ -1971,6 +2050,7 @@ class TypstWriter {
|
|
|
1971
2050
|
}
|
|
1972
2051
|
this.buffer += str;
|
|
1973
2052
|
}
|
|
2053
|
+
// Serialize a tree of TypstNode into a list of TypstToken
|
|
1974
2054
|
serialize(node) {
|
|
1975
2055
|
switch (node.type) {
|
|
1976
2056
|
case "empty":
|
|
@@ -1998,8 +2078,7 @@ class TypstWriter {
|
|
|
1998
2078
|
if (this.keepSpaces) {
|
|
1999
2079
|
this.queue.push(new TypstToken(4 /* SPACE */, c));
|
|
2000
2080
|
}
|
|
2001
|
-
} else if (c ===
|
|
2002
|
-
`) {
|
|
2081
|
+
} else if (c === "\n") {
|
|
2003
2082
|
this.queue.push(new TypstToken(0 /* SYMBOL */, c));
|
|
2004
2083
|
} else {
|
|
2005
2084
|
throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
|
|
@@ -2038,7 +2117,7 @@ class TypstWriter {
|
|
|
2038
2117
|
this.queue.push(func_symbol);
|
|
2039
2118
|
this.insideFunctionDepth++;
|
|
2040
2119
|
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2041
|
-
for (let i = 0;i < node.args.length; i++) {
|
|
2120
|
+
for (let i = 0; i < node.args.length; i++) {
|
|
2042
2121
|
this.serialize(node.args[i]);
|
|
2043
2122
|
if (i < node.args.length - 1) {
|
|
2044
2123
|
this.queue.push(new TypstToken(1 /* ELEMENT */, ","));
|
|
@@ -2046,7 +2125,8 @@ class TypstWriter {
|
|
|
2046
2125
|
}
|
|
2047
2126
|
if (node.options) {
|
|
2048
2127
|
for (const [key, value] of Object.entries(node.options)) {
|
|
2049
|
-
|
|
2128
|
+
const value_str = typst_primitive_to_string(value);
|
|
2129
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, `, ${key}: ${value_str}`));
|
|
2050
2130
|
}
|
|
2051
2131
|
}
|
|
2052
2132
|
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
@@ -2094,7 +2174,8 @@ class TypstWriter {
|
|
|
2094
2174
|
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2095
2175
|
if (node.options) {
|
|
2096
2176
|
for (const [key, value] of Object.entries(node.options)) {
|
|
2097
|
-
|
|
2177
|
+
const value_str = typst_primitive_to_string(value);
|
|
2178
|
+
this.queue.push(new TypstToken(0 /* SYMBOL */, `${key}: ${value_str}, `));
|
|
2098
2179
|
}
|
|
2099
2180
|
}
|
|
2100
2181
|
matrix.forEach((row, i) => {
|
|
@@ -2145,7 +2226,7 @@ class TypstWriter {
|
|
|
2145
2226
|
}
|
|
2146
2227
|
flushQueue() {
|
|
2147
2228
|
const SOFT_SPACE = new TypstToken(6 /* CONTROL */, " ");
|
|
2148
|
-
for (let i = 0;i < this.queue.length; i++) {
|
|
2229
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
2149
2230
|
let token = this.queue[i];
|
|
2150
2231
|
if (token.eq(SOFT_SPACE)) {
|
|
2151
2232
|
if (i === this.queue.length - 1) {
|
|
@@ -2183,7 +2264,7 @@ class TypstWriter {
|
|
|
2183
2264
|
}
|
|
2184
2265
|
return this.buffer;
|
|
2185
2266
|
}
|
|
2186
|
-
}
|
|
2267
|
+
};
|
|
2187
2268
|
|
|
2188
2269
|
// src/convert.ts
|
|
2189
2270
|
function tex_token_to_typst(token) {
|
|
@@ -2228,12 +2309,21 @@ function convert_overset(node, options) {
|
|
|
2228
2309
|
if (is_def(sup) && is_eq(base)) {
|
|
2229
2310
|
return new TypstNode("symbol", "eq.def");
|
|
2230
2311
|
}
|
|
2231
|
-
const op_call = new TypstNode(
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
base
|
|
2235
|
-
|
|
2236
|
-
});
|
|
2312
|
+
const op_call = new TypstNode(
|
|
2313
|
+
"funcCall",
|
|
2314
|
+
"op",
|
|
2315
|
+
[convert_tex_node_to_typst(base, options)]
|
|
2316
|
+
);
|
|
2317
|
+
op_call.setOptions({ limits: TYPST_TRUE });
|
|
2318
|
+
return new TypstNode(
|
|
2319
|
+
"supsub",
|
|
2320
|
+
"",
|
|
2321
|
+
[],
|
|
2322
|
+
{
|
|
2323
|
+
base: op_call,
|
|
2324
|
+
sup: convert_tex_node_to_typst(sup, options)
|
|
2325
|
+
}
|
|
2326
|
+
);
|
|
2237
2327
|
}
|
|
2238
2328
|
function convert_tex_node_to_typst(node, options = {}) {
|
|
2239
2329
|
switch (node.type) {
|
|
@@ -2242,7 +2332,11 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2242
2332
|
case "whitespace":
|
|
2243
2333
|
return new TypstNode("whitespace", node.content);
|
|
2244
2334
|
case "ordgroup":
|
|
2245
|
-
return new TypstNode(
|
|
2335
|
+
return new TypstNode(
|
|
2336
|
+
"group",
|
|
2337
|
+
"",
|
|
2338
|
+
node.args.map((n) => convert_tex_node_to_typst(n, options))
|
|
2339
|
+
);
|
|
2246
2340
|
case "element":
|
|
2247
2341
|
return new TypstNode("atom", tex_token_to_typst(node.content));
|
|
2248
2342
|
case "symbol":
|
|
@@ -2254,9 +2348,17 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2254
2348
|
case "supsub": {
|
|
2255
2349
|
let { base, sup, sub } = node.data;
|
|
2256
2350
|
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
2257
|
-
return new TypstNode(
|
|
2351
|
+
return new TypstNode(
|
|
2352
|
+
"funcCall",
|
|
2353
|
+
"overbrace",
|
|
2354
|
+
[convert_tex_node_to_typst(base.args[0], options), convert_tex_node_to_typst(sup, options)]
|
|
2355
|
+
);
|
|
2258
2356
|
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
2259
|
-
return new TypstNode(
|
|
2357
|
+
return new TypstNode(
|
|
2358
|
+
"funcCall",
|
|
2359
|
+
"underbrace",
|
|
2360
|
+
[convert_tex_node_to_typst(base.args[0], options), convert_tex_node_to_typst(sub, options)]
|
|
2361
|
+
);
|
|
2260
2362
|
}
|
|
2261
2363
|
const data = {
|
|
2262
2364
|
base: convert_tex_node_to_typst(base, options)
|
|
@@ -2274,7 +2376,11 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2274
2376
|
}
|
|
2275
2377
|
case "leftright": {
|
|
2276
2378
|
const [left, body, right] = node.args;
|
|
2277
|
-
const group = new TypstNode(
|
|
2379
|
+
const group = new TypstNode(
|
|
2380
|
+
"group",
|
|
2381
|
+
"",
|
|
2382
|
+
node.args.map((n) => convert_tex_node_to_typst(n, options))
|
|
2383
|
+
);
|
|
2278
2384
|
if ([
|
|
2279
2385
|
"[]",
|
|
2280
2386
|
"()",
|
|
@@ -2292,7 +2398,11 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2292
2398
|
group.args.shift();
|
|
2293
2399
|
return new TypstNode("funcCall", "lr", [group]);
|
|
2294
2400
|
}
|
|
2295
|
-
return new TypstNode(
|
|
2401
|
+
return new TypstNode(
|
|
2402
|
+
"funcCall",
|
|
2403
|
+
"lr",
|
|
2404
|
+
[group]
|
|
2405
|
+
);
|
|
2296
2406
|
}
|
|
2297
2407
|
case "binaryFunc": {
|
|
2298
2408
|
if (node.content === "\\overset") {
|
|
@@ -2300,20 +2410,40 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2300
2410
|
}
|
|
2301
2411
|
if (node.content === "\\frac") {
|
|
2302
2412
|
if (options.fracToSlash) {
|
|
2303
|
-
return new TypstNode(
|
|
2413
|
+
return new TypstNode(
|
|
2414
|
+
"fraction",
|
|
2415
|
+
"",
|
|
2416
|
+
node.args.map((n) => convert_tex_node_to_typst(n, options))
|
|
2417
|
+
);
|
|
2304
2418
|
}
|
|
2305
2419
|
}
|
|
2306
|
-
return new TypstNode(
|
|
2420
|
+
return new TypstNode(
|
|
2421
|
+
"funcCall",
|
|
2422
|
+
tex_token_to_typst(node.content),
|
|
2423
|
+
node.args.map((n) => convert_tex_node_to_typst(n, options))
|
|
2424
|
+
);
|
|
2307
2425
|
}
|
|
2308
2426
|
case "unaryFunc": {
|
|
2309
2427
|
const arg0 = convert_tex_node_to_typst(node.args[0], options);
|
|
2310
2428
|
if (node.content === "\\sqrt" && node.data) {
|
|
2311
2429
|
const data = convert_tex_node_to_typst(node.data, options);
|
|
2312
|
-
return new TypstNode(
|
|
2430
|
+
return new TypstNode(
|
|
2431
|
+
"funcCall",
|
|
2432
|
+
"root",
|
|
2433
|
+
[data, arg0]
|
|
2434
|
+
);
|
|
2313
2435
|
}
|
|
2314
2436
|
if (node.content === "\\mathbf") {
|
|
2315
|
-
const inner = new TypstNode(
|
|
2316
|
-
|
|
2437
|
+
const inner = new TypstNode(
|
|
2438
|
+
"funcCall",
|
|
2439
|
+
"bold",
|
|
2440
|
+
[arg0]
|
|
2441
|
+
);
|
|
2442
|
+
return new TypstNode(
|
|
2443
|
+
"funcCall",
|
|
2444
|
+
"upright",
|
|
2445
|
+
[inner]
|
|
2446
|
+
);
|
|
2317
2447
|
}
|
|
2318
2448
|
if (node.content === "\\mathbb" && arg0.type === "atom" && /^[A-Z]$/.test(arg0.content)) {
|
|
2319
2449
|
return new TypstNode("symbol", arg0.content + arg0.content);
|
|
@@ -2327,21 +2457,61 @@ function convert_tex_node_to_typst(node, options = {}) {
|
|
|
2327
2457
|
if (TYPST_INTRINSIC_SYMBOLS.includes(text)) {
|
|
2328
2458
|
return new TypstNode("symbol", text);
|
|
2329
2459
|
} else {
|
|
2330
|
-
return new TypstNode(
|
|
2460
|
+
return new TypstNode(
|
|
2461
|
+
"funcCall",
|
|
2462
|
+
"op",
|
|
2463
|
+
[new TypstNode("text", text)]
|
|
2464
|
+
);
|
|
2331
2465
|
}
|
|
2332
2466
|
}
|
|
2333
|
-
return new TypstNode(
|
|
2467
|
+
return new TypstNode(
|
|
2468
|
+
"funcCall",
|
|
2469
|
+
tex_token_to_typst(node.content),
|
|
2470
|
+
node.args.map((n) => convert_tex_node_to_typst(n, options))
|
|
2471
|
+
);
|
|
2334
2472
|
}
|
|
2335
2473
|
case "beginend": {
|
|
2336
2474
|
const matrix = node.data;
|
|
2337
2475
|
const data = matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2338
2476
|
if (node.content.startsWith("align")) {
|
|
2339
2477
|
return new TypstNode("align", "", [], data);
|
|
2340
|
-
}
|
|
2478
|
+
}
|
|
2479
|
+
if (node.content.endsWith("matrix")) {
|
|
2480
|
+
let delim = null;
|
|
2481
|
+
switch (node.content) {
|
|
2482
|
+
case "matrix":
|
|
2483
|
+
delim = TYPST_NONE;
|
|
2484
|
+
break;
|
|
2485
|
+
case "pmatrix":
|
|
2486
|
+
delim = "(";
|
|
2487
|
+
break;
|
|
2488
|
+
case "bmatrix":
|
|
2489
|
+
delim = "[";
|
|
2490
|
+
break;
|
|
2491
|
+
case "Bmatrix":
|
|
2492
|
+
delim = "{";
|
|
2493
|
+
break;
|
|
2494
|
+
case "vmatrix":
|
|
2495
|
+
delim = "|";
|
|
2496
|
+
break;
|
|
2497
|
+
case "Vmatrix": {
|
|
2498
|
+
const matrix2 = new TypstNode("matrix", "", [], data);
|
|
2499
|
+
matrix2.setOptions({ "delim": TYPST_NONE });
|
|
2500
|
+
const group = new TypstNode("group", "", [
|
|
2501
|
+
new TypstNode("symbol", "||"),
|
|
2502
|
+
matrix2,
|
|
2503
|
+
new TypstNode("symbol", "||")
|
|
2504
|
+
]);
|
|
2505
|
+
return new TypstNode("funcCall", "lr", [group]);
|
|
2506
|
+
}
|
|
2507
|
+
default:
|
|
2508
|
+
throw new TypstWriterError(`Unimplemented beginend: ${node.content}`, node);
|
|
2509
|
+
}
|
|
2341
2510
|
const res = new TypstNode("matrix", "", [], data);
|
|
2342
|
-
res.setOptions({ delim:
|
|
2511
|
+
res.setOptions({ "delim": delim });
|
|
2343
2512
|
return res;
|
|
2344
2513
|
}
|
|
2514
|
+
throw new TypstWriterError(`Unimplemented beginend: ${node.content}`, node);
|
|
2345
2515
|
}
|
|
2346
2516
|
case "unknownMacro":
|
|
2347
2517
|
return new TypstNode("unknown", tex_token_to_typst(node.content));
|
|
@@ -2413,8 +2583,10 @@ function convert_typst_node_to_tex(node) {
|
|
|
2413
2583
|
return new TexNode("element", node.content);
|
|
2414
2584
|
case "symbol":
|
|
2415
2585
|
switch (node.content) {
|
|
2586
|
+
// special hook for comma
|
|
2416
2587
|
case "comma":
|
|
2417
2588
|
return new TexNode("element", ",");
|
|
2589
|
+
// special hook for hyph and hyph.minus
|
|
2418
2590
|
case "hyph":
|
|
2419
2591
|
case "hyph.minus":
|
|
2420
2592
|
return new TexNode("text", "-");
|
|
@@ -2498,7 +2670,7 @@ function convert_typst_node_to_tex(node) {
|
|
|
2498
2670
|
if (node.options) {
|
|
2499
2671
|
if ("delim" in node.options) {
|
|
2500
2672
|
switch (node.options.delim) {
|
|
2501
|
-
case
|
|
2673
|
+
case TYPST_NONE:
|
|
2502
2674
|
return matrix;
|
|
2503
2675
|
case "[":
|
|
2504
2676
|
left_delim = "\\left[";
|
|
@@ -2587,20 +2759,16 @@ function tokenize_typst(typst) {
|
|
|
2587
2759
|
token = new TypstToken(6 /* CONTROL */, firstChar);
|
|
2588
2760
|
pos++;
|
|
2589
2761
|
break;
|
|
2590
|
-
case
|
|
2591
|
-
`:
|
|
2762
|
+
case "\n":
|
|
2592
2763
|
token = new TypstToken(7 /* NEWLINE */, firstChar);
|
|
2593
2764
|
pos++;
|
|
2594
2765
|
break;
|
|
2595
2766
|
case "\r": {
|
|
2596
|
-
if (pos + 1 < typst.length && typst[pos + 1] ===
|
|
2597
|
-
|
|
2598
|
-
token = new TypstToken(7 /* NEWLINE */, `
|
|
2599
|
-
`);
|
|
2767
|
+
if (pos + 1 < typst.length && typst[pos + 1] === "\n") {
|
|
2768
|
+
token = new TypstToken(7 /* NEWLINE */, "\n");
|
|
2600
2769
|
pos += 2;
|
|
2601
2770
|
} else {
|
|
2602
|
-
token = new TypstToken(7 /* NEWLINE */,
|
|
2603
|
-
`);
|
|
2771
|
+
token = new TypstToken(7 /* NEWLINE */, "\n");
|
|
2604
2772
|
pos++;
|
|
2605
2773
|
}
|
|
2606
2774
|
break;
|
|
@@ -2617,8 +2785,7 @@ function tokenize_typst(typst) {
|
|
|
2617
2785
|
case "/": {
|
|
2618
2786
|
if (pos < typst.length && typst[pos + 1] === "/") {
|
|
2619
2787
|
let newPos = pos + 2;
|
|
2620
|
-
while (newPos < typst.length && typst[newPos] !==
|
|
2621
|
-
`) {
|
|
2788
|
+
while (newPos < typst.length && typst[newPos] !== "\n") {
|
|
2622
2789
|
newPos++;
|
|
2623
2790
|
}
|
|
2624
2791
|
token = new TypstToken(3 /* COMMENT */, typst.slice(pos + 2, newPos));
|
|
@@ -2730,7 +2897,7 @@ function find_closing_parenthesis(nodes, start) {
|
|
|
2730
2897
|
}
|
|
2731
2898
|
function primes(num) {
|
|
2732
2899
|
const res = [];
|
|
2733
|
-
for (let i = 0;i < num; i++) {
|
|
2900
|
+
for (let i = 0; i < num; i++) {
|
|
2734
2901
|
res.push(new TypstNode("atom", "'"));
|
|
2735
2902
|
}
|
|
2736
2903
|
return res;
|
|
@@ -2746,7 +2913,7 @@ function next_non_whitespace(nodes, start) {
|
|
|
2746
2913
|
function trim_whitespace_around_operators(nodes) {
|
|
2747
2914
|
let after_operator = false;
|
|
2748
2915
|
const res = [];
|
|
2749
|
-
for (let i = 0;i < nodes.length; i++) {
|
|
2916
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
2750
2917
|
const current = nodes[i];
|
|
2751
2918
|
if (current.type === "whitespace") {
|
|
2752
2919
|
if (after_operator) {
|
|
@@ -2820,13 +2987,12 @@ function process_operators(nodes, parenthesis = false) {
|
|
|
2820
2987
|
}
|
|
2821
2988
|
}
|
|
2822
2989
|
}
|
|
2823
|
-
|
|
2824
|
-
class TypstParserError extends Error {
|
|
2990
|
+
var TypstParserError = class extends Error {
|
|
2825
2991
|
constructor(message) {
|
|
2826
2992
|
super(message);
|
|
2827
2993
|
this.name = "TypstParserError";
|
|
2828
2994
|
}
|
|
2829
|
-
}
|
|
2995
|
+
};
|
|
2830
2996
|
var SUB_SYMBOL2 = new TypstToken(6 /* CONTROL */, "_");
|
|
2831
2997
|
var SUP_SYMBOL2 = new TypstToken(6 /* CONTROL */, "^");
|
|
2832
2998
|
var LEFT_PARENTHESES = new TypstToken(1 /* ELEMENT */, "(");
|
|
@@ -2838,10 +3004,7 @@ var RIGHT_CURLY_BRACKET2 = new TypstToken(1 /* ELEMENT */, "}");
|
|
|
2838
3004
|
var COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
2839
3005
|
var SEMICOLON = new TypstToken(1 /* ELEMENT */, ";");
|
|
2840
3006
|
var SINGLE_SPACE = new TypstToken(4 /* SPACE */, " ");
|
|
2841
|
-
|
|
2842
|
-
class TypstParser {
|
|
2843
|
-
space_sensitive;
|
|
2844
|
-
newline_sensitive;
|
|
3007
|
+
var TypstParser = class {
|
|
2845
3008
|
constructor(space_sensitive = true, newline_sensitive = true) {
|
|
2846
3009
|
this.space_sensitive = space_sensitive;
|
|
2847
3010
|
this.newline_sensitive = newline_sensitive;
|
|
@@ -2860,8 +3023,7 @@ class TypstParser {
|
|
|
2860
3023
|
if (!this.space_sensitive && res.content.replace(/ /g, "").length === 0) {
|
|
2861
3024
|
continue;
|
|
2862
3025
|
}
|
|
2863
|
-
if (!this.newline_sensitive && res.content ===
|
|
2864
|
-
`) {
|
|
3026
|
+
if (!this.newline_sensitive && res.content === "\n") {
|
|
2865
3027
|
continue;
|
|
2866
3028
|
}
|
|
2867
3029
|
}
|
|
@@ -2956,10 +3118,12 @@ class TypstParser {
|
|
|
2956
3118
|
}
|
|
2957
3119
|
return [node, start + 1];
|
|
2958
3120
|
}
|
|
3121
|
+
// start: the position of the left parentheses
|
|
2959
3122
|
parseArguments(tokens, start) {
|
|
2960
3123
|
const end = find_closing_match2(tokens, start);
|
|
2961
3124
|
return [this.parseCommaSeparatedArguments(tokens, start + 1, end), end + 1];
|
|
2962
3125
|
}
|
|
3126
|
+
// start: the position of the left parentheses
|
|
2963
3127
|
parseGroupsOfArguments(tokens, start) {
|
|
2964
3128
|
const end = find_closing_match2(tokens, start);
|
|
2965
3129
|
tokens = tokens.slice(0, end);
|
|
@@ -2968,11 +3132,11 @@ class TypstParser {
|
|
|
2968
3132
|
let pos = start + 1;
|
|
2969
3133
|
while (pos < end) {
|
|
2970
3134
|
while (pos < end) {
|
|
2971
|
-
let
|
|
3135
|
+
let extract_named_params2 = function(arr) {
|
|
2972
3136
|
const COLON = new TypstNode("atom", ":");
|
|
2973
3137
|
const np2 = {};
|
|
2974
3138
|
const to_delete = [];
|
|
2975
|
-
for (let i = 0;i < arr.length; i++) {
|
|
3139
|
+
for (let i = 0; i < arr.length; i++) {
|
|
2976
3140
|
if (arr[i].type !== "group") {
|
|
2977
3141
|
continue;
|
|
2978
3142
|
}
|
|
@@ -2993,7 +3157,7 @@ class TypstParser {
|
|
|
2993
3157
|
if (g.args.length !== 4 || !g.args[pos_colon + 2].eq(new TypstNode("symbol", "none"))) {
|
|
2994
3158
|
throw new TypstParserError("Invalid number of arguments for delim");
|
|
2995
3159
|
}
|
|
2996
|
-
np2["delim"] =
|
|
3160
|
+
np2["delim"] = TYPST_NONE;
|
|
2997
3161
|
} else {
|
|
2998
3162
|
throw new TypstParserError("Not implemented for other types of delim");
|
|
2999
3163
|
}
|
|
@@ -3001,18 +3165,19 @@ class TypstParser {
|
|
|
3001
3165
|
throw new TypstParserError("Not implemented for other named parameters");
|
|
3002
3166
|
}
|
|
3003
3167
|
}
|
|
3004
|
-
for (let i = to_delete.length - 1;i >= 0; i--) {
|
|
3168
|
+
for (let i = to_delete.length - 1; i >= 0; i--) {
|
|
3005
3169
|
arr.splice(to_delete[i], 1);
|
|
3006
3170
|
}
|
|
3007
3171
|
return [arr, np2];
|
|
3008
3172
|
};
|
|
3173
|
+
var extract_named_params = extract_named_params2;
|
|
3009
3174
|
let next_stop = array_find(tokens, SEMICOLON, pos);
|
|
3010
3175
|
if (next_stop === -1) {
|
|
3011
3176
|
next_stop = end;
|
|
3012
3177
|
}
|
|
3013
3178
|
let row = this.parseCommaSeparatedArguments(tokens, pos, next_stop);
|
|
3014
3179
|
let np = {};
|
|
3015
|
-
[row, np] =
|
|
3180
|
+
[row, np] = extract_named_params2(row);
|
|
3016
3181
|
matrix.push(row);
|
|
3017
3182
|
Object.assign(named_params, np);
|
|
3018
3183
|
pos = next_stop + 1;
|
|
@@ -3020,6 +3185,7 @@ class TypstParser {
|
|
|
3020
3185
|
}
|
|
3021
3186
|
return [matrix, named_params, end + 1];
|
|
3022
3187
|
}
|
|
3188
|
+
// start: the position of the first token of arguments
|
|
3023
3189
|
parseCommaSeparatedArguments(tokens, start, end) {
|
|
3024
3190
|
const args = [];
|
|
3025
3191
|
let pos = start;
|
|
@@ -3046,17 +3212,19 @@ class TypstParser {
|
|
|
3046
3212
|
}
|
|
3047
3213
|
return args;
|
|
3048
3214
|
}
|
|
3049
|
-
}
|
|
3215
|
+
};
|
|
3050
3216
|
function parseTypst(typst) {
|
|
3051
|
-
const parser = new TypstParser;
|
|
3217
|
+
const parser = new TypstParser();
|
|
3052
3218
|
let tokens = tokenize_typst(typst);
|
|
3053
3219
|
return parser.parse(tokens);
|
|
3054
3220
|
}
|
|
3055
3221
|
|
|
3056
3222
|
// src/tex-writer.ts
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3223
|
+
var TexWriter = class {
|
|
3224
|
+
constructor() {
|
|
3225
|
+
this.buffer = "";
|
|
3226
|
+
this.queue = [];
|
|
3227
|
+
}
|
|
3060
3228
|
writeBuffer(token) {
|
|
3061
3229
|
const str = token.toString();
|
|
3062
3230
|
let no_need_space = false;
|
|
@@ -3095,7 +3263,7 @@ class TexWriter {
|
|
|
3095
3263
|
this.queue = this.queue.concat(node.serialize());
|
|
3096
3264
|
}
|
|
3097
3265
|
flushQueue() {
|
|
3098
|
-
for (let i = 0;i < this.queue.length; i++) {
|
|
3266
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
3099
3267
|
this.writeBuffer(this.queue[i]);
|
|
3100
3268
|
}
|
|
3101
3269
|
this.queue = [];
|
|
@@ -3104,7 +3272,7 @@ class TexWriter {
|
|
|
3104
3272
|
this.flushQueue();
|
|
3105
3273
|
return this.buffer;
|
|
3106
3274
|
}
|
|
3107
|
-
}
|
|
3275
|
+
};
|
|
3108
3276
|
|
|
3109
3277
|
// src/index.ts
|
|
3110
3278
|
function tex2typst(tex, options) {
|
|
@@ -3125,7 +3293,7 @@ function tex2typst(tex, options) {
|
|
|
3125
3293
|
if (options.customTexMacros) {
|
|
3126
3294
|
opt.customTexMacros = options.customTexMacros;
|
|
3127
3295
|
}
|
|
3128
|
-
if (options.fracToSlash !==
|
|
3296
|
+
if (options.fracToSlash !== void 0) {
|
|
3129
3297
|
opt.fracToSlash = options.fracToSlash;
|
|
3130
3298
|
}
|
|
3131
3299
|
}
|
|
@@ -3138,12 +3306,12 @@ function tex2typst(tex, options) {
|
|
|
3138
3306
|
function typst2tex(typst) {
|
|
3139
3307
|
const typstTree = parseTypst(typst);
|
|
3140
3308
|
const texTree = convert_typst_node_to_tex(typstTree);
|
|
3141
|
-
const writer = new TexWriter;
|
|
3309
|
+
const writer = new TexWriter();
|
|
3142
3310
|
writer.append(texTree);
|
|
3143
3311
|
return writer.finalize();
|
|
3144
3312
|
}
|
|
3145
3313
|
export {
|
|
3146
|
-
|
|
3314
|
+
symbolMap,
|
|
3147
3315
|
tex2typst,
|
|
3148
|
-
|
|
3316
|
+
typst2tex
|
|
3149
3317
|
};
|