tex2typst 0.3.0-beta-5 → 0.3.0-beta-7
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 +17 -35
- package/dist/convert.d.ts +3 -0
- package/dist/index.js +296 -265
- package/dist/tex-writer.d.ts +1 -3
- package/dist/tex2typst.min.js +14 -14
- package/dist/types.d.ts +1 -0
- package/dist/{writer.d.ts → typst-writer.d.ts} +1 -1
- package/package.json +1 -1
- package/src/convert.ts +478 -0
- package/src/index.ts +8 -3
- package/src/tex-writer.ts +1 -211
- package/src/types.ts +1 -0
- package/src/{writer.ts → typst-writer.ts} +26 -253
package/dist/index.js
CHANGED
|
@@ -1906,7 +1906,7 @@ function parseTex(tex, customTexMacros) {
|
|
|
1906
1906
|
return parser.parse(tokens);
|
|
1907
1907
|
}
|
|
1908
1908
|
|
|
1909
|
-
// src/writer.ts
|
|
1909
|
+
// src/typst-writer.ts
|
|
1910
1910
|
var TYPST_INTRINSIC_SYMBOLS = [
|
|
1911
1911
|
"dim",
|
|
1912
1912
|
"id",
|
|
@@ -1919,34 +1919,6 @@ var TYPST_INTRINSIC_SYMBOLS = [
|
|
|
1919
1919
|
function is_delimiter(c) {
|
|
1920
1920
|
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "⌊", "⌋", "⌈", "⌉"].includes(c.content);
|
|
1921
1921
|
}
|
|
1922
|
-
function convert_overset(node) {
|
|
1923
|
-
const [sup, base] = node.args;
|
|
1924
|
-
const is_def = (n) => {
|
|
1925
|
-
if (n.eq(new TexNode("text", "def"))) {
|
|
1926
|
-
return true;
|
|
1927
|
-
}
|
|
1928
|
-
if (n.type === "ordgroup" && n.args.length === 3) {
|
|
1929
|
-
const [a1, a2, a3] = n.args;
|
|
1930
|
-
const d = new TexNode("element", "d");
|
|
1931
|
-
const e = new TexNode("element", "e");
|
|
1932
|
-
const f = new TexNode("element", "f");
|
|
1933
|
-
if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
|
|
1934
|
-
return true;
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
return false;
|
|
1938
|
-
};
|
|
1939
|
-
const is_eq = (n) => n.eq(new TexNode("element", "="));
|
|
1940
|
-
if (is_def(sup) && is_eq(base)) {
|
|
1941
|
-
return new TypstNode("symbol", "eq.def");
|
|
1942
|
-
}
|
|
1943
|
-
const op_call = new TypstNode("funcCall", "op", [convertTree(base)]);
|
|
1944
|
-
op_call.setOptions({ limits: "#true" });
|
|
1945
|
-
return new TypstNode("supsub", "", [], {
|
|
1946
|
-
base: op_call,
|
|
1947
|
-
sup: convertTree(sup)
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
1922
|
var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
|
|
1951
1923
|
var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
|
|
1952
1924
|
var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
@@ -1982,7 +1954,8 @@ class TypstWriter {
|
|
|
1982
1954
|
let no_need_space = false;
|
|
1983
1955
|
no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
1984
1956
|
no_need_space ||= /^[})\]\|]$/.test(str);
|
|
1985
|
-
no_need_space ||=
|
|
1957
|
+
no_need_space ||= /[^=]$/.test(this.buffer) && str === "(";
|
|
1958
|
+
no_need_space ||= /^[_^,;!]$/.test(str);
|
|
1986
1959
|
no_need_space ||= str === "'";
|
|
1987
1960
|
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1988
1961
|
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
@@ -1991,6 +1964,7 @@ class TypstWriter {
|
|
|
1991
1964
|
no_need_space ||= this.buffer === "";
|
|
1992
1965
|
no_need_space ||= /^\s/.test(str);
|
|
1993
1966
|
no_need_space ||= this.buffer.endsWith("&") && str === "=";
|
|
1967
|
+
no_need_space ||= this.buffer.endsWith("/") || str === "/";
|
|
1994
1968
|
no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
|
|
1995
1969
|
if (!no_need_space) {
|
|
1996
1970
|
this.buffer += " ";
|
|
@@ -2079,6 +2053,25 @@ class TypstWriter {
|
|
|
2079
2053
|
this.insideFunctionDepth--;
|
|
2080
2054
|
break;
|
|
2081
2055
|
}
|
|
2056
|
+
case "fraction": {
|
|
2057
|
+
const [numerator, denominator] = node.args;
|
|
2058
|
+
if (numerator.type === "group") {
|
|
2059
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2060
|
+
this.serialize(numerator);
|
|
2061
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2062
|
+
} else {
|
|
2063
|
+
this.serialize(numerator);
|
|
2064
|
+
}
|
|
2065
|
+
this.queue.push(new TypstToken(1 /* ELEMENT */, "/"));
|
|
2066
|
+
if (denominator.type === "group") {
|
|
2067
|
+
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
2068
|
+
this.serialize(denominator);
|
|
2069
|
+
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
2070
|
+
} else {
|
|
2071
|
+
this.serialize(denominator);
|
|
2072
|
+
}
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2082
2075
|
case "align": {
|
|
2083
2076
|
const matrix = node.data;
|
|
2084
2077
|
matrix.forEach((row, i) => {
|
|
@@ -2191,18 +2184,69 @@ class TypstWriter {
|
|
|
2191
2184
|
return this.buffer;
|
|
2192
2185
|
}
|
|
2193
2186
|
}
|
|
2194
|
-
|
|
2187
|
+
|
|
2188
|
+
// src/convert.ts
|
|
2189
|
+
function tex_token_to_typst(token) {
|
|
2190
|
+
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
2191
|
+
return token;
|
|
2192
|
+
} else if (token === "/") {
|
|
2193
|
+
return "\\/";
|
|
2194
|
+
} else if (token === "\\|") {
|
|
2195
|
+
return "parallel";
|
|
2196
|
+
} else if (token === "\\\\") {
|
|
2197
|
+
return "\\";
|
|
2198
|
+
} else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
|
|
2199
|
+
return token;
|
|
2200
|
+
} else if (token.startsWith("\\")) {
|
|
2201
|
+
const symbol = token.slice(1);
|
|
2202
|
+
if (symbolMap.has(symbol)) {
|
|
2203
|
+
return symbolMap.get(symbol);
|
|
2204
|
+
} else {
|
|
2205
|
+
return symbol;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
return token;
|
|
2209
|
+
}
|
|
2210
|
+
function convert_overset(node, options) {
|
|
2211
|
+
const [sup, base] = node.args;
|
|
2212
|
+
const is_def = (n) => {
|
|
2213
|
+
if (n.eq(new TexNode("text", "def"))) {
|
|
2214
|
+
return true;
|
|
2215
|
+
}
|
|
2216
|
+
if (n.type === "ordgroup" && n.args.length === 3) {
|
|
2217
|
+
const [a1, a2, a3] = n.args;
|
|
2218
|
+
const d = new TexNode("element", "d");
|
|
2219
|
+
const e = new TexNode("element", "e");
|
|
2220
|
+
const f = new TexNode("element", "f");
|
|
2221
|
+
if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
|
|
2222
|
+
return true;
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
return false;
|
|
2226
|
+
};
|
|
2227
|
+
const is_eq = (n) => n.eq(new TexNode("element", "="));
|
|
2228
|
+
if (is_def(sup) && is_eq(base)) {
|
|
2229
|
+
return new TypstNode("symbol", "eq.def");
|
|
2230
|
+
}
|
|
2231
|
+
const op_call = new TypstNode("funcCall", "op", [convert_tex_node_to_typst(base, options)]);
|
|
2232
|
+
op_call.setOptions({ limits: "#true" });
|
|
2233
|
+
return new TypstNode("supsub", "", [], {
|
|
2234
|
+
base: op_call,
|
|
2235
|
+
sup: convert_tex_node_to_typst(sup, options)
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
function convert_tex_node_to_typst(node, options = {}) {
|
|
2195
2239
|
switch (node.type) {
|
|
2196
2240
|
case "empty":
|
|
2197
2241
|
return new TypstNode("empty", "");
|
|
2198
2242
|
case "whitespace":
|
|
2199
2243
|
return new TypstNode("whitespace", node.content);
|
|
2200
2244
|
case "ordgroup":
|
|
2201
|
-
return new TypstNode("group", "", node.args.map(
|
|
2245
|
+
return new TypstNode("group", "", node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2202
2246
|
case "element":
|
|
2203
|
-
return new TypstNode("atom",
|
|
2247
|
+
return new TypstNode("atom", tex_token_to_typst(node.content));
|
|
2204
2248
|
case "symbol":
|
|
2205
|
-
return new TypstNode("symbol",
|
|
2249
|
+
return new TypstNode("symbol", tex_token_to_typst(node.content));
|
|
2206
2250
|
case "text":
|
|
2207
2251
|
return new TypstNode("text", node.content);
|
|
2208
2252
|
case "comment":
|
|
@@ -2210,27 +2254,27 @@ function convertTree(node) {
|
|
|
2210
2254
|
case "supsub": {
|
|
2211
2255
|
let { base, sup, sub } = node.data;
|
|
2212
2256
|
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
2213
|
-
return new TypstNode("funcCall", "overbrace", [
|
|
2257
|
+
return new TypstNode("funcCall", "overbrace", [convert_tex_node_to_typst(base.args[0], options), convert_tex_node_to_typst(sup, options)]);
|
|
2214
2258
|
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
2215
|
-
return new TypstNode("funcCall", "underbrace", [
|
|
2259
|
+
return new TypstNode("funcCall", "underbrace", [convert_tex_node_to_typst(base.args[0], options), convert_tex_node_to_typst(sub, options)]);
|
|
2216
2260
|
}
|
|
2217
2261
|
const data = {
|
|
2218
|
-
base:
|
|
2262
|
+
base: convert_tex_node_to_typst(base, options)
|
|
2219
2263
|
};
|
|
2220
2264
|
if (data.base.type === "empty") {
|
|
2221
2265
|
data.base = new TypstNode("text", "");
|
|
2222
2266
|
}
|
|
2223
2267
|
if (sup) {
|
|
2224
|
-
data.sup =
|
|
2268
|
+
data.sup = convert_tex_node_to_typst(sup, options);
|
|
2225
2269
|
}
|
|
2226
2270
|
if (sub) {
|
|
2227
|
-
data.sub =
|
|
2271
|
+
data.sub = convert_tex_node_to_typst(sub, options);
|
|
2228
2272
|
}
|
|
2229
2273
|
return new TypstNode("supsub", "", [], data);
|
|
2230
2274
|
}
|
|
2231
2275
|
case "leftright": {
|
|
2232
2276
|
const [left, body, right] = node.args;
|
|
2233
|
-
const group = new TypstNode("group", "", node.args.map(
|
|
2277
|
+
const group = new TypstNode("group", "", node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2234
2278
|
if ([
|
|
2235
2279
|
"[]",
|
|
2236
2280
|
"()",
|
|
@@ -2252,14 +2296,19 @@ function convertTree(node) {
|
|
|
2252
2296
|
}
|
|
2253
2297
|
case "binaryFunc": {
|
|
2254
2298
|
if (node.content === "\\overset") {
|
|
2255
|
-
return convert_overset(node);
|
|
2299
|
+
return convert_overset(node, options);
|
|
2256
2300
|
}
|
|
2257
|
-
|
|
2301
|
+
if (node.content === "\\frac") {
|
|
2302
|
+
if (options.fracToSlash) {
|
|
2303
|
+
return new TypstNode("fraction", "", node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return new TypstNode("funcCall", tex_token_to_typst(node.content), node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2258
2307
|
}
|
|
2259
2308
|
case "unaryFunc": {
|
|
2260
|
-
const arg0 =
|
|
2309
|
+
const arg0 = convert_tex_node_to_typst(node.args[0], options);
|
|
2261
2310
|
if (node.content === "\\sqrt" && node.data) {
|
|
2262
|
-
const data =
|
|
2311
|
+
const data = convert_tex_node_to_typst(node.data, options);
|
|
2263
2312
|
return new TypstNode("funcCall", "root", [data, arg0]);
|
|
2264
2313
|
}
|
|
2265
2314
|
if (node.content === "\\mathbf") {
|
|
@@ -2281,11 +2330,11 @@ function convertTree(node) {
|
|
|
2281
2330
|
return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
|
|
2282
2331
|
}
|
|
2283
2332
|
}
|
|
2284
|
-
return new TypstNode("funcCall",
|
|
2333
|
+
return new TypstNode("funcCall", tex_token_to_typst(node.content), node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2285
2334
|
}
|
|
2286
2335
|
case "beginend": {
|
|
2287
2336
|
const matrix = node.data;
|
|
2288
|
-
const data = matrix.map((row) => row.map(
|
|
2337
|
+
const data = matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2289
2338
|
if (node.content.startsWith("align")) {
|
|
2290
2339
|
return new TypstNode("align", "", [], data);
|
|
2291
2340
|
} else {
|
|
@@ -2295,7 +2344,7 @@ function convertTree(node) {
|
|
|
2295
2344
|
}
|
|
2296
2345
|
}
|
|
2297
2346
|
case "unknownMacro":
|
|
2298
|
-
return new TypstNode("unknown",
|
|
2347
|
+
return new TypstNode("unknown", tex_token_to_typst(node.content));
|
|
2299
2348
|
case "control":
|
|
2300
2349
|
if (node.content === "\\\\") {
|
|
2301
2350
|
return new TypstNode("symbol", "\\");
|
|
@@ -2308,26 +2357,205 @@ function convertTree(node) {
|
|
|
2308
2357
|
throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
|
|
2309
2358
|
}
|
|
2310
2359
|
}
|
|
2311
|
-
|
|
2360
|
+
var TYPST_UNARY_FUNCTIONS = [
|
|
2361
|
+
"sqrt",
|
|
2362
|
+
"bold",
|
|
2363
|
+
"arrow",
|
|
2364
|
+
"upright",
|
|
2365
|
+
"lr",
|
|
2366
|
+
"op",
|
|
2367
|
+
"macron",
|
|
2368
|
+
"dot",
|
|
2369
|
+
"dot.double",
|
|
2370
|
+
"hat",
|
|
2371
|
+
"tilde",
|
|
2372
|
+
"overline",
|
|
2373
|
+
"underline",
|
|
2374
|
+
"bb",
|
|
2375
|
+
"cal",
|
|
2376
|
+
"frak"
|
|
2377
|
+
];
|
|
2378
|
+
var TYPST_BINARY_FUNCTIONS = [
|
|
2379
|
+
"frac",
|
|
2380
|
+
"root",
|
|
2381
|
+
"overbrace",
|
|
2382
|
+
"underbrace"
|
|
2383
|
+
];
|
|
2384
|
+
function apply_escape_if_needed2(c) {
|
|
2385
|
+
if (["{", "}", "%"].includes(c)) {
|
|
2386
|
+
return "\\" + c;
|
|
2387
|
+
}
|
|
2388
|
+
return c;
|
|
2389
|
+
}
|
|
2390
|
+
function typst_token_to_tex(token) {
|
|
2312
2391
|
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
2313
2392
|
return token;
|
|
2314
|
-
} else if (token === "
|
|
2315
|
-
return "
|
|
2316
|
-
} else if (token
|
|
2317
|
-
return "
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2393
|
+
} else if (token === "thin") {
|
|
2394
|
+
return "\\,";
|
|
2395
|
+
} else if (reverseSymbolMap.has(token)) {
|
|
2396
|
+
return "\\" + reverseSymbolMap.get(token);
|
|
2397
|
+
}
|
|
2398
|
+
return "\\" + token;
|
|
2399
|
+
}
|
|
2400
|
+
function convert_typst_node_to_tex(node) {
|
|
2401
|
+
if (node.eq(new TypstNode("symbol", "eq.def"))) {
|
|
2402
|
+
return new TexNode("binaryFunc", "\\overset", [
|
|
2403
|
+
new TexNode("text", "def"),
|
|
2404
|
+
new TexNode("element", "=")
|
|
2405
|
+
]);
|
|
2406
|
+
}
|
|
2407
|
+
switch (node.type) {
|
|
2408
|
+
case "empty":
|
|
2409
|
+
return new TexNode("empty", "");
|
|
2410
|
+
case "whitespace":
|
|
2411
|
+
return new TexNode("whitespace", node.content);
|
|
2412
|
+
case "atom":
|
|
2413
|
+
return new TexNode("element", node.content);
|
|
2414
|
+
case "symbol":
|
|
2415
|
+
switch (node.content) {
|
|
2416
|
+
case "comma":
|
|
2417
|
+
return new TexNode("element", ",");
|
|
2418
|
+
case "hyph":
|
|
2419
|
+
case "hyph.minus":
|
|
2420
|
+
return new TexNode("text", "-");
|
|
2421
|
+
default:
|
|
2422
|
+
return new TexNode("symbol", typst_token_to_tex(node.content));
|
|
2423
|
+
}
|
|
2424
|
+
case "text":
|
|
2425
|
+
return new TexNode("text", node.content);
|
|
2426
|
+
case "comment":
|
|
2427
|
+
return new TexNode("comment", node.content);
|
|
2428
|
+
case "group": {
|
|
2429
|
+
const args = node.args.map(convert_typst_node_to_tex);
|
|
2430
|
+
return new TexNode("ordgroup", node.content, args);
|
|
2328
2431
|
}
|
|
2432
|
+
case "funcCall": {
|
|
2433
|
+
if (TYPST_UNARY_FUNCTIONS.includes(node.content)) {
|
|
2434
|
+
if (node.content === "lr") {
|
|
2435
|
+
const body = node.args[0];
|
|
2436
|
+
if (body.type === "group") {
|
|
2437
|
+
let left_delim = body.args[0].content;
|
|
2438
|
+
let right_delim = body.args[body.args.length - 1].content;
|
|
2439
|
+
left_delim = apply_escape_if_needed2(left_delim);
|
|
2440
|
+
right_delim = apply_escape_if_needed2(right_delim);
|
|
2441
|
+
return new TexNode("ordgroup", "", [
|
|
2442
|
+
new TexNode("element", "\\left" + left_delim),
|
|
2443
|
+
...body.args.slice(1, body.args.length - 1).map(convert_typst_node_to_tex),
|
|
2444
|
+
new TexNode("element", "\\right" + right_delim)
|
|
2445
|
+
]);
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
const command = typst_token_to_tex(node.content);
|
|
2449
|
+
return new TexNode("unaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2450
|
+
} else if (TYPST_BINARY_FUNCTIONS.includes(node.content)) {
|
|
2451
|
+
if (node.content === "root") {
|
|
2452
|
+
const [degree, radicand] = node.args;
|
|
2453
|
+
const data = convert_typst_node_to_tex(degree);
|
|
2454
|
+
return new TexNode("unaryFunc", "\\sqrt", [convert_typst_node_to_tex(radicand)], data);
|
|
2455
|
+
}
|
|
2456
|
+
if (node.content === "overbrace" || node.content === "underbrace") {
|
|
2457
|
+
const [body, label] = node.args;
|
|
2458
|
+
const base = new TexNode("unaryFunc", "\\" + node.content, [convert_typst_node_to_tex(body)]);
|
|
2459
|
+
const script = convert_typst_node_to_tex(label);
|
|
2460
|
+
const data = node.content === "overbrace" ? { base, sup: script } : { base, sub: script };
|
|
2461
|
+
return new TexNode("supsub", "", [], data);
|
|
2462
|
+
}
|
|
2463
|
+
const command = typst_token_to_tex(node.content);
|
|
2464
|
+
return new TexNode("binaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2465
|
+
} else {
|
|
2466
|
+
return new TexNode("ordgroup", "", [
|
|
2467
|
+
new TexNode("symbol", typst_token_to_tex(node.content)),
|
|
2468
|
+
new TexNode("element", "("),
|
|
2469
|
+
...node.args.map(convert_typst_node_to_tex),
|
|
2470
|
+
new TexNode("element", ")")
|
|
2471
|
+
]);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
case "supsub": {
|
|
2475
|
+
const { base, sup, sub } = node.data;
|
|
2476
|
+
const base_tex = convert_typst_node_to_tex(base);
|
|
2477
|
+
let sup_tex;
|
|
2478
|
+
let sub_tex;
|
|
2479
|
+
if (sup) {
|
|
2480
|
+
sup_tex = convert_typst_node_to_tex(sup);
|
|
2481
|
+
}
|
|
2482
|
+
if (sub) {
|
|
2483
|
+
sub_tex = convert_typst_node_to_tex(sub);
|
|
2484
|
+
}
|
|
2485
|
+
const res = new TexNode("supsub", "", [], {
|
|
2486
|
+
base: base_tex,
|
|
2487
|
+
sup: sup_tex,
|
|
2488
|
+
sub: sub_tex
|
|
2489
|
+
});
|
|
2490
|
+
return res;
|
|
2491
|
+
}
|
|
2492
|
+
case "matrix": {
|
|
2493
|
+
const typst_data = node.data;
|
|
2494
|
+
const tex_data = typst_data.map((row) => row.map(convert_typst_node_to_tex));
|
|
2495
|
+
const matrix = new TexNode("beginend", "matrix", [], tex_data);
|
|
2496
|
+
let left_delim = "\\left(";
|
|
2497
|
+
let right_delim = "\\right)";
|
|
2498
|
+
if (node.options) {
|
|
2499
|
+
if ("delim" in node.options) {
|
|
2500
|
+
switch (node.options.delim) {
|
|
2501
|
+
case "#none":
|
|
2502
|
+
return matrix;
|
|
2503
|
+
case "[":
|
|
2504
|
+
left_delim = "\\left[";
|
|
2505
|
+
right_delim = "\\right]";
|
|
2506
|
+
break;
|
|
2507
|
+
case "]":
|
|
2508
|
+
left_delim = "\\left]";
|
|
2509
|
+
right_delim = "\\right[";
|
|
2510
|
+
break;
|
|
2511
|
+
case "{":
|
|
2512
|
+
left_delim = "\\left\\{";
|
|
2513
|
+
right_delim = "\\right\\}";
|
|
2514
|
+
break;
|
|
2515
|
+
case "}":
|
|
2516
|
+
left_delim = "\\left\\}";
|
|
2517
|
+
right_delim = "\\right\\{";
|
|
2518
|
+
break;
|
|
2519
|
+
case "|":
|
|
2520
|
+
left_delim = "\\left|";
|
|
2521
|
+
right_delim = "\\right|";
|
|
2522
|
+
break;
|
|
2523
|
+
case ")":
|
|
2524
|
+
left_delim = "\\left)";
|
|
2525
|
+
right_delim = "\\right(";
|
|
2526
|
+
case "(":
|
|
2527
|
+
default:
|
|
2528
|
+
left_delim = "\\left(";
|
|
2529
|
+
right_delim = "\\right)";
|
|
2530
|
+
break;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
return new TexNode("ordgroup", "", [
|
|
2535
|
+
new TexNode("element", left_delim),
|
|
2536
|
+
matrix,
|
|
2537
|
+
new TexNode("element", right_delim)
|
|
2538
|
+
]);
|
|
2539
|
+
}
|
|
2540
|
+
case "control": {
|
|
2541
|
+
switch (node.content) {
|
|
2542
|
+
case "\\":
|
|
2543
|
+
return new TexNode("control", "\\\\");
|
|
2544
|
+
case "&":
|
|
2545
|
+
return new TexNode("control", "&");
|
|
2546
|
+
default:
|
|
2547
|
+
throw new Error("[convert_typst_node_to_tex] Unimplemented control: " + node.content);
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
case "fraction": {
|
|
2551
|
+
const [numerator, denominator] = node.args;
|
|
2552
|
+
const num_tex = convert_typst_node_to_tex(numerator);
|
|
2553
|
+
const den_tex = convert_typst_node_to_tex(denominator);
|
|
2554
|
+
return new TexNode("binaryFunc", "\\frac", [num_tex, den_tex]);
|
|
2555
|
+
}
|
|
2556
|
+
default:
|
|
2557
|
+
throw new Error("[convert_typst_node_to_tex] Unimplemented type: " + node.type);
|
|
2329
2558
|
}
|
|
2330
|
-
return token;
|
|
2331
2559
|
}
|
|
2332
2560
|
|
|
2333
2561
|
// src/typst-parser.ts
|
|
@@ -2826,37 +3054,6 @@ function parseTypst(typst) {
|
|
|
2826
3054
|
}
|
|
2827
3055
|
|
|
2828
3056
|
// src/tex-writer.ts
|
|
2829
|
-
var TYPST_UNARY_FUNCTIONS = [
|
|
2830
|
-
"sqrt",
|
|
2831
|
-
"bold",
|
|
2832
|
-
"arrow",
|
|
2833
|
-
"upright",
|
|
2834
|
-
"lr",
|
|
2835
|
-
"op",
|
|
2836
|
-
"macron",
|
|
2837
|
-
"dot",
|
|
2838
|
-
"dot.double",
|
|
2839
|
-
"hat",
|
|
2840
|
-
"tilde",
|
|
2841
|
-
"overline",
|
|
2842
|
-
"underline",
|
|
2843
|
-
"bb",
|
|
2844
|
-
"cal",
|
|
2845
|
-
"frak"
|
|
2846
|
-
];
|
|
2847
|
-
var TYPST_BINARY_FUNCTIONS = [
|
|
2848
|
-
"frac",
|
|
2849
|
-
"root",
|
|
2850
|
-
"overbrace",
|
|
2851
|
-
"underbrace"
|
|
2852
|
-
];
|
|
2853
|
-
function apply_escape_if_needed2(c) {
|
|
2854
|
-
if (["{", "}", "%"].includes(c)) {
|
|
2855
|
-
return "\\" + c;
|
|
2856
|
-
}
|
|
2857
|
-
return c;
|
|
2858
|
-
}
|
|
2859
|
-
|
|
2860
3057
|
class TexWriter {
|
|
2861
3058
|
buffer = "";
|
|
2862
3059
|
queue = [];
|
|
@@ -2908,176 +3105,6 @@ class TexWriter {
|
|
|
2908
3105
|
return this.buffer;
|
|
2909
3106
|
}
|
|
2910
3107
|
}
|
|
2911
|
-
function convert_typst_node_to_tex(node) {
|
|
2912
|
-
if (node.eq(new TypstNode("symbol", "eq.def"))) {
|
|
2913
|
-
return new TexNode("binaryFunc", "\\overset", [
|
|
2914
|
-
new TexNode("text", "def"),
|
|
2915
|
-
new TexNode("element", "=")
|
|
2916
|
-
]);
|
|
2917
|
-
}
|
|
2918
|
-
switch (node.type) {
|
|
2919
|
-
case "empty":
|
|
2920
|
-
return new TexNode("empty", "");
|
|
2921
|
-
case "whitespace":
|
|
2922
|
-
return new TexNode("whitespace", node.content);
|
|
2923
|
-
case "atom":
|
|
2924
|
-
return new TexNode("element", node.content);
|
|
2925
|
-
case "symbol":
|
|
2926
|
-
switch (node.content) {
|
|
2927
|
-
case "comma":
|
|
2928
|
-
return new TexNode("element", ",");
|
|
2929
|
-
case "hyph":
|
|
2930
|
-
case "hyph.minus":
|
|
2931
|
-
return new TexNode("text", "-");
|
|
2932
|
-
default:
|
|
2933
|
-
return new TexNode("symbol", typst_token_to_tex(node.content));
|
|
2934
|
-
}
|
|
2935
|
-
case "text":
|
|
2936
|
-
return new TexNode("text", node.content);
|
|
2937
|
-
case "comment":
|
|
2938
|
-
return new TexNode("comment", node.content);
|
|
2939
|
-
case "group": {
|
|
2940
|
-
const args = node.args.map(convert_typst_node_to_tex);
|
|
2941
|
-
return new TexNode("ordgroup", node.content, args);
|
|
2942
|
-
}
|
|
2943
|
-
case "funcCall": {
|
|
2944
|
-
if (TYPST_UNARY_FUNCTIONS.includes(node.content)) {
|
|
2945
|
-
if (node.content === "lr") {
|
|
2946
|
-
const body = node.args[0];
|
|
2947
|
-
if (body.type === "group") {
|
|
2948
|
-
let left_delim = body.args[0].content;
|
|
2949
|
-
let right_delim = body.args[body.args.length - 1].content;
|
|
2950
|
-
left_delim = apply_escape_if_needed2(left_delim);
|
|
2951
|
-
right_delim = apply_escape_if_needed2(right_delim);
|
|
2952
|
-
return new TexNode("ordgroup", "", [
|
|
2953
|
-
new TexNode("element", "\\left" + left_delim),
|
|
2954
|
-
...body.args.slice(1, body.args.length - 1).map(convert_typst_node_to_tex),
|
|
2955
|
-
new TexNode("element", "\\right" + right_delim)
|
|
2956
|
-
]);
|
|
2957
|
-
}
|
|
2958
|
-
}
|
|
2959
|
-
const command = typst_token_to_tex(node.content);
|
|
2960
|
-
return new TexNode("unaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2961
|
-
} else if (TYPST_BINARY_FUNCTIONS.includes(node.content)) {
|
|
2962
|
-
if (node.content === "root") {
|
|
2963
|
-
const [degree, radicand] = node.args;
|
|
2964
|
-
const data = convert_typst_node_to_tex(degree);
|
|
2965
|
-
return new TexNode("unaryFunc", "\\sqrt", [convert_typst_node_to_tex(radicand)], data);
|
|
2966
|
-
}
|
|
2967
|
-
if (node.content === "overbrace" || node.content === "underbrace") {
|
|
2968
|
-
const [body, label] = node.args;
|
|
2969
|
-
const base = new TexNode("unaryFunc", "\\" + node.content, [convert_typst_node_to_tex(body)]);
|
|
2970
|
-
const script = convert_typst_node_to_tex(label);
|
|
2971
|
-
const data = node.content === "overbrace" ? { base, sup: script } : { base, sub: script };
|
|
2972
|
-
return new TexNode("supsub", "", [], data);
|
|
2973
|
-
}
|
|
2974
|
-
const command = typst_token_to_tex(node.content);
|
|
2975
|
-
return new TexNode("binaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2976
|
-
} else {
|
|
2977
|
-
return new TexNode("ordgroup", "", [
|
|
2978
|
-
new TexNode("symbol", typst_token_to_tex(node.content)),
|
|
2979
|
-
new TexNode("element", "("),
|
|
2980
|
-
...node.args.map(convert_typst_node_to_tex),
|
|
2981
|
-
new TexNode("element", ")")
|
|
2982
|
-
]);
|
|
2983
|
-
}
|
|
2984
|
-
}
|
|
2985
|
-
case "supsub": {
|
|
2986
|
-
const { base, sup, sub } = node.data;
|
|
2987
|
-
const base_tex = convert_typst_node_to_tex(base);
|
|
2988
|
-
let sup_tex;
|
|
2989
|
-
let sub_tex;
|
|
2990
|
-
if (sup) {
|
|
2991
|
-
sup_tex = convert_typst_node_to_tex(sup);
|
|
2992
|
-
}
|
|
2993
|
-
if (sub) {
|
|
2994
|
-
sub_tex = convert_typst_node_to_tex(sub);
|
|
2995
|
-
}
|
|
2996
|
-
const res = new TexNode("supsub", "", [], {
|
|
2997
|
-
base: base_tex,
|
|
2998
|
-
sup: sup_tex,
|
|
2999
|
-
sub: sub_tex
|
|
3000
|
-
});
|
|
3001
|
-
return res;
|
|
3002
|
-
}
|
|
3003
|
-
case "matrix": {
|
|
3004
|
-
const typst_data = node.data;
|
|
3005
|
-
const tex_data = typst_data.map((row) => row.map(convert_typst_node_to_tex));
|
|
3006
|
-
const matrix = new TexNode("beginend", "matrix", [], tex_data);
|
|
3007
|
-
let left_delim = "\\left(";
|
|
3008
|
-
let right_delim = "\\right)";
|
|
3009
|
-
if (node.options) {
|
|
3010
|
-
if ("delim" in node.options) {
|
|
3011
|
-
switch (node.options.delim) {
|
|
3012
|
-
case "#none":
|
|
3013
|
-
return matrix;
|
|
3014
|
-
case "[":
|
|
3015
|
-
left_delim = "\\left[";
|
|
3016
|
-
right_delim = "\\right]";
|
|
3017
|
-
break;
|
|
3018
|
-
case "]":
|
|
3019
|
-
left_delim = "\\left]";
|
|
3020
|
-
right_delim = "\\right[";
|
|
3021
|
-
break;
|
|
3022
|
-
case "{":
|
|
3023
|
-
left_delim = "\\left\\{";
|
|
3024
|
-
right_delim = "\\right\\}";
|
|
3025
|
-
break;
|
|
3026
|
-
case "}":
|
|
3027
|
-
left_delim = "\\left\\}";
|
|
3028
|
-
right_delim = "\\right\\{";
|
|
3029
|
-
break;
|
|
3030
|
-
case "|":
|
|
3031
|
-
left_delim = "\\left|";
|
|
3032
|
-
right_delim = "\\right|";
|
|
3033
|
-
break;
|
|
3034
|
-
case ")":
|
|
3035
|
-
left_delim = "\\left)";
|
|
3036
|
-
right_delim = "\\right(";
|
|
3037
|
-
case "(":
|
|
3038
|
-
default:
|
|
3039
|
-
left_delim = "\\left(";
|
|
3040
|
-
right_delim = "\\right)";
|
|
3041
|
-
break;
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
return new TexNode("ordgroup", "", [
|
|
3046
|
-
new TexNode("element", left_delim),
|
|
3047
|
-
matrix,
|
|
3048
|
-
new TexNode("element", right_delim)
|
|
3049
|
-
]);
|
|
3050
|
-
}
|
|
3051
|
-
case "control": {
|
|
3052
|
-
switch (node.content) {
|
|
3053
|
-
case "\\":
|
|
3054
|
-
return new TexNode("control", "\\\\");
|
|
3055
|
-
case "&":
|
|
3056
|
-
return new TexNode("control", "&");
|
|
3057
|
-
default:
|
|
3058
|
-
throw new Error("[convert_typst_node_to_tex] Unimplemented control: " + node.content);
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
case "fraction": {
|
|
3062
|
-
const [numerator, denominator] = node.args;
|
|
3063
|
-
const num_tex = convert_typst_node_to_tex(numerator);
|
|
3064
|
-
const den_tex = convert_typst_node_to_tex(denominator);
|
|
3065
|
-
return new TexNode("binaryFunc", "\\frac", [num_tex, den_tex]);
|
|
3066
|
-
}
|
|
3067
|
-
default:
|
|
3068
|
-
throw new Error("[convert_typst_node_to_tex] Unimplemented type: " + node.type);
|
|
3069
|
-
}
|
|
3070
|
-
}
|
|
3071
|
-
function typst_token_to_tex(token) {
|
|
3072
|
-
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
3073
|
-
return token;
|
|
3074
|
-
} else if (token === "thin") {
|
|
3075
|
-
return "\\,";
|
|
3076
|
-
} else if (reverseSymbolMap.has(token)) {
|
|
3077
|
-
return "\\" + reverseSymbolMap.get(token);
|
|
3078
|
-
}
|
|
3079
|
-
return "\\" + token;
|
|
3080
|
-
}
|
|
3081
3108
|
|
|
3082
3109
|
// src/index.ts
|
|
3083
3110
|
function tex2typst(tex, options) {
|
|
@@ -3085,6 +3112,7 @@ function tex2typst(tex, options) {
|
|
|
3085
3112
|
nonStrict: true,
|
|
3086
3113
|
preferTypstIntrinsic: true,
|
|
3087
3114
|
keepSpaces: false,
|
|
3115
|
+
fracToSlash: true,
|
|
3088
3116
|
customTexMacros: {}
|
|
3089
3117
|
};
|
|
3090
3118
|
if (options) {
|
|
@@ -3097,9 +3125,12 @@ function tex2typst(tex, options) {
|
|
|
3097
3125
|
if (options.customTexMacros) {
|
|
3098
3126
|
opt.customTexMacros = options.customTexMacros;
|
|
3099
3127
|
}
|
|
3128
|
+
if (options.fracToSlash !== undefined) {
|
|
3129
|
+
opt.fracToSlash = options.fracToSlash;
|
|
3130
|
+
}
|
|
3100
3131
|
}
|
|
3101
3132
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
3102
|
-
const typstTree =
|
|
3133
|
+
const typstTree = convert_tex_node_to_typst(texTree, opt);
|
|
3103
3134
|
const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
|
|
3104
3135
|
writer.serialize(typstTree);
|
|
3105
3136
|
return writer.finalize();
|