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/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 ||= /^[(_^,;!]$/.test(str);
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
- function convertTree(node) {
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(convertTree));
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", convertToken(node.content));
2247
+ return new TypstNode("atom", tex_token_to_typst(node.content));
2204
2248
  case "symbol":
2205
- return new TypstNode("symbol", convertToken(node.content));
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", [convertTree(base.args[0]), convertTree(sup)]);
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", [convertTree(base.args[0]), convertTree(sub)]);
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: convertTree(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 = convertTree(sup);
2268
+ data.sup = convert_tex_node_to_typst(sup, options);
2225
2269
  }
2226
2270
  if (sub) {
2227
- data.sub = convertTree(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(convertTree));
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
- return new TypstNode("funcCall", convertToken(node.content), node.args.map(convertTree));
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 = convertTree(node.args[0]);
2309
+ const arg0 = convert_tex_node_to_typst(node.args[0], options);
2261
2310
  if (node.content === "\\sqrt" && node.data) {
2262
- const data = convertTree(node.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", convertToken(node.content), node.args.map(convertTree));
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(convertTree));
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", convertToken(node.content));
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
- function convertToken(token) {
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 "parallel";
2318
- } else if (token === "\\\\") {
2319
- return "\\";
2320
- } else if (["\\$", "\\#", "\\&", "\\_"].includes(token)) {
2321
- return token;
2322
- } else if (token.startsWith("\\")) {
2323
- const symbol = token.slice(1);
2324
- if (symbolMap.has(symbol)) {
2325
- return symbolMap.get(symbol);
2326
- } else {
2327
- return symbol;
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 = convertTree(texTree);
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();