tex2typst 0.3.0-beta-4 → 0.3.0-beta-6
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 +328 -271
- package/dist/tex-writer.d.ts +1 -3
- package/dist/tex2typst.min.js +14 -14
- package/dist/types.d.ts +2 -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 +5 -3
- package/src/tex-writer.ts +1 -215
- package/src/types.ts +40 -2
- package/src/{writer.ts → typst-writer.ts} +26 -253
package/dist/index.js
CHANGED
|
@@ -1100,8 +1100,17 @@ class TexNode {
|
|
|
1100
1100
|
}
|
|
1101
1101
|
return tokens;
|
|
1102
1102
|
}
|
|
1103
|
-
case "ordgroup":
|
|
1104
|
-
|
|
1103
|
+
case "ordgroup": {
|
|
1104
|
+
let tokens = this.args.map((n) => n.serialize()).flat();
|
|
1105
|
+
if (this.content === "parenthesis") {
|
|
1106
|
+
const is_over_high = this.isOverHigh();
|
|
1107
|
+
const left_delim = is_over_high ? "\\left(" : "(";
|
|
1108
|
+
const right_delim = is_over_high ? "\\right)" : ")";
|
|
1109
|
+
tokens.unshift(new TexToken(0 /* ELEMENT */, left_delim));
|
|
1110
|
+
tokens.push(new TexToken(0 /* ELEMENT */, right_delim));
|
|
1111
|
+
}
|
|
1112
|
+
return tokens;
|
|
1113
|
+
}
|
|
1105
1114
|
case "unaryFunc": {
|
|
1106
1115
|
let tokens = [];
|
|
1107
1116
|
tokens.push(new TexToken(1 /* COMMAND */, this.content));
|
|
@@ -1199,6 +1208,30 @@ class TexNode {
|
|
|
1199
1208
|
throw new Error("[TexNode.serialize] Unimplemented type: " + this.type);
|
|
1200
1209
|
}
|
|
1201
1210
|
}
|
|
1211
|
+
isOverHigh() {
|
|
1212
|
+
switch (this.type) {
|
|
1213
|
+
case "element":
|
|
1214
|
+
case "symbol":
|
|
1215
|
+
case "text":
|
|
1216
|
+
case "control":
|
|
1217
|
+
case "empty":
|
|
1218
|
+
return false;
|
|
1219
|
+
case "binaryFunc":
|
|
1220
|
+
if (this.content === "\\frac") {
|
|
1221
|
+
return true;
|
|
1222
|
+
}
|
|
1223
|
+
case "unaryFunc":
|
|
1224
|
+
case "ordgroup":
|
|
1225
|
+
return this.args.some((n) => n.isOverHigh());
|
|
1226
|
+
case "supsub": {
|
|
1227
|
+
return this.data.base.isOverHigh();
|
|
1228
|
+
}
|
|
1229
|
+
case "beginend":
|
|
1230
|
+
return true;
|
|
1231
|
+
default:
|
|
1232
|
+
return false;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1202
1235
|
}
|
|
1203
1236
|
class TypstToken {
|
|
1204
1237
|
type;
|
|
@@ -1873,7 +1906,7 @@ function parseTex(tex, customTexMacros) {
|
|
|
1873
1906
|
return parser.parse(tokens);
|
|
1874
1907
|
}
|
|
1875
1908
|
|
|
1876
|
-
// src/writer.ts
|
|
1909
|
+
// src/typst-writer.ts
|
|
1877
1910
|
var TYPST_INTRINSIC_SYMBOLS = [
|
|
1878
1911
|
"dim",
|
|
1879
1912
|
"id",
|
|
@@ -1886,34 +1919,6 @@ var TYPST_INTRINSIC_SYMBOLS = [
|
|
|
1886
1919
|
function is_delimiter(c) {
|
|
1887
1920
|
return c.type === "atom" && ["(", ")", "[", "]", "{", "}", "|", "⌊", "⌋", "⌈", "⌉"].includes(c.content);
|
|
1888
1921
|
}
|
|
1889
|
-
function convert_overset(node) {
|
|
1890
|
-
const [sup, base] = node.args;
|
|
1891
|
-
const is_def = (n) => {
|
|
1892
|
-
if (n.eq(new TexNode("text", "def"))) {
|
|
1893
|
-
return true;
|
|
1894
|
-
}
|
|
1895
|
-
if (n.type === "ordgroup" && n.args.length === 3) {
|
|
1896
|
-
const [a1, a2, a3] = n.args;
|
|
1897
|
-
const d = new TexNode("element", "d");
|
|
1898
|
-
const e = new TexNode("element", "e");
|
|
1899
|
-
const f = new TexNode("element", "f");
|
|
1900
|
-
if (a1.eq(d) && a2.eq(e) && a3.eq(f)) {
|
|
1901
|
-
return true;
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1904
|
-
return false;
|
|
1905
|
-
};
|
|
1906
|
-
const is_eq = (n) => n.eq(new TexNode("element", "="));
|
|
1907
|
-
if (is_def(sup) && is_eq(base)) {
|
|
1908
|
-
return new TypstNode("symbol", "eq.def");
|
|
1909
|
-
}
|
|
1910
|
-
const op_call = new TypstNode("funcCall", "op", [convertTree(base)]);
|
|
1911
|
-
op_call.setOptions({ limits: "#true" });
|
|
1912
|
-
return new TypstNode("supsub", "", [], {
|
|
1913
|
-
base: op_call,
|
|
1914
|
-
sup: convertTree(sup)
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
1922
|
var TYPST_LEFT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, "(");
|
|
1918
1923
|
var TYPST_RIGHT_PARENTHESIS = new TypstToken(1 /* ELEMENT */, ")");
|
|
1919
1924
|
var TYPST_COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
@@ -1949,7 +1954,8 @@ class TypstWriter {
|
|
|
1949
1954
|
let no_need_space = false;
|
|
1950
1955
|
no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
1951
1956
|
no_need_space ||= /^[})\]\|]$/.test(str);
|
|
1952
|
-
no_need_space ||=
|
|
1957
|
+
no_need_space ||= /[^=]$/.test(this.buffer) && str === "(";
|
|
1958
|
+
no_need_space ||= /^[_^,;!]$/.test(str);
|
|
1953
1959
|
no_need_space ||= str === "'";
|
|
1954
1960
|
no_need_space ||= /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str);
|
|
1955
1961
|
no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
|
|
@@ -1958,6 +1964,7 @@ class TypstWriter {
|
|
|
1958
1964
|
no_need_space ||= this.buffer === "";
|
|
1959
1965
|
no_need_space ||= /^\s/.test(str);
|
|
1960
1966
|
no_need_space ||= this.buffer.endsWith("&") && str === "=";
|
|
1967
|
+
no_need_space ||= this.buffer.endsWith("/") || str === "/";
|
|
1961
1968
|
no_need_space ||= /[\s_^{\(]$/.test(this.buffer);
|
|
1962
1969
|
if (!no_need_space) {
|
|
1963
1970
|
this.buffer += " ";
|
|
@@ -2046,6 +2053,25 @@ class TypstWriter {
|
|
|
2046
2053
|
this.insideFunctionDepth--;
|
|
2047
2054
|
break;
|
|
2048
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
|
+
}
|
|
2049
2075
|
case "align": {
|
|
2050
2076
|
const matrix = node.data;
|
|
2051
2077
|
matrix.forEach((row, i) => {
|
|
@@ -2158,18 +2184,69 @@ class TypstWriter {
|
|
|
2158
2184
|
return this.buffer;
|
|
2159
2185
|
}
|
|
2160
2186
|
}
|
|
2161
|
-
|
|
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 = {}) {
|
|
2162
2239
|
switch (node.type) {
|
|
2163
2240
|
case "empty":
|
|
2164
2241
|
return new TypstNode("empty", "");
|
|
2165
2242
|
case "whitespace":
|
|
2166
2243
|
return new TypstNode("whitespace", node.content);
|
|
2167
2244
|
case "ordgroup":
|
|
2168
|
-
return new TypstNode("group", "", node.args.map(
|
|
2245
|
+
return new TypstNode("group", "", node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2169
2246
|
case "element":
|
|
2170
|
-
return new TypstNode("atom",
|
|
2247
|
+
return new TypstNode("atom", tex_token_to_typst(node.content));
|
|
2171
2248
|
case "symbol":
|
|
2172
|
-
return new TypstNode("symbol",
|
|
2249
|
+
return new TypstNode("symbol", tex_token_to_typst(node.content));
|
|
2173
2250
|
case "text":
|
|
2174
2251
|
return new TypstNode("text", node.content);
|
|
2175
2252
|
case "comment":
|
|
@@ -2177,27 +2254,27 @@ function convertTree(node) {
|
|
|
2177
2254
|
case "supsub": {
|
|
2178
2255
|
let { base, sup, sub } = node.data;
|
|
2179
2256
|
if (base && base.type === "unaryFunc" && base.content === "\\overbrace" && sup) {
|
|
2180
|
-
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)]);
|
|
2181
2258
|
} else if (base && base.type === "unaryFunc" && base.content === "\\underbrace" && sub) {
|
|
2182
|
-
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)]);
|
|
2183
2260
|
}
|
|
2184
2261
|
const data = {
|
|
2185
|
-
base:
|
|
2262
|
+
base: convert_tex_node_to_typst(base, options)
|
|
2186
2263
|
};
|
|
2187
2264
|
if (data.base.type === "empty") {
|
|
2188
2265
|
data.base = new TypstNode("text", "");
|
|
2189
2266
|
}
|
|
2190
2267
|
if (sup) {
|
|
2191
|
-
data.sup =
|
|
2268
|
+
data.sup = convert_tex_node_to_typst(sup, options);
|
|
2192
2269
|
}
|
|
2193
2270
|
if (sub) {
|
|
2194
|
-
data.sub =
|
|
2271
|
+
data.sub = convert_tex_node_to_typst(sub, options);
|
|
2195
2272
|
}
|
|
2196
2273
|
return new TypstNode("supsub", "", [], data);
|
|
2197
2274
|
}
|
|
2198
2275
|
case "leftright": {
|
|
2199
2276
|
const [left, body, right] = node.args;
|
|
2200
|
-
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)));
|
|
2201
2278
|
if ([
|
|
2202
2279
|
"[]",
|
|
2203
2280
|
"()",
|
|
@@ -2219,14 +2296,19 @@ function convertTree(node) {
|
|
|
2219
2296
|
}
|
|
2220
2297
|
case "binaryFunc": {
|
|
2221
2298
|
if (node.content === "\\overset") {
|
|
2222
|
-
return convert_overset(node);
|
|
2299
|
+
return convert_overset(node, options);
|
|
2300
|
+
}
|
|
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
|
+
}
|
|
2223
2305
|
}
|
|
2224
|
-
return new TypstNode("funcCall",
|
|
2306
|
+
return new TypstNode("funcCall", tex_token_to_typst(node.content), node.args.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2225
2307
|
}
|
|
2226
2308
|
case "unaryFunc": {
|
|
2227
|
-
const arg0 =
|
|
2309
|
+
const arg0 = convert_tex_node_to_typst(node.args[0], options);
|
|
2228
2310
|
if (node.content === "\\sqrt" && node.data) {
|
|
2229
|
-
const data =
|
|
2311
|
+
const data = convert_tex_node_to_typst(node.data, options);
|
|
2230
2312
|
return new TypstNode("funcCall", "root", [data, arg0]);
|
|
2231
2313
|
}
|
|
2232
2314
|
if (node.content === "\\mathbf") {
|
|
@@ -2248,11 +2330,11 @@ function convertTree(node) {
|
|
|
2248
2330
|
return new TypstNode("funcCall", "op", [new TypstNode("text", text)]);
|
|
2249
2331
|
}
|
|
2250
2332
|
}
|
|
2251
|
-
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)));
|
|
2252
2334
|
}
|
|
2253
2335
|
case "beginend": {
|
|
2254
2336
|
const matrix = node.data;
|
|
2255
|
-
const data = matrix.map((row) => row.map(
|
|
2337
|
+
const data = matrix.map((row) => row.map((n) => convert_tex_node_to_typst(n, options)));
|
|
2256
2338
|
if (node.content.startsWith("align")) {
|
|
2257
2339
|
return new TypstNode("align", "", [], data);
|
|
2258
2340
|
} else {
|
|
@@ -2262,7 +2344,7 @@ function convertTree(node) {
|
|
|
2262
2344
|
}
|
|
2263
2345
|
}
|
|
2264
2346
|
case "unknownMacro":
|
|
2265
|
-
return new TypstNode("unknown",
|
|
2347
|
+
return new TypstNode("unknown", tex_token_to_typst(node.content));
|
|
2266
2348
|
case "control":
|
|
2267
2349
|
if (node.content === "\\\\") {
|
|
2268
2350
|
return new TypstNode("symbol", "\\");
|
|
@@ -2275,26 +2357,205 @@ function convertTree(node) {
|
|
|
2275
2357
|
throw new TypstWriterError(`Unimplemented node type: ${node.type}`, node);
|
|
2276
2358
|
}
|
|
2277
2359
|
}
|
|
2278
|
-
|
|
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) {
|
|
2279
2391
|
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
2280
2392
|
return token;
|
|
2281
|
-
} else if (token === "
|
|
2282
|
-
return "
|
|
2283
|
-
} else if (token
|
|
2284
|
-
return "
|
|
2285
|
-
}
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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);
|
|
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
|
+
}
|
|
2295
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);
|
|
2296
2558
|
}
|
|
2297
|
-
return token;
|
|
2298
2559
|
}
|
|
2299
2560
|
|
|
2300
2561
|
// src/typst-parser.ts
|
|
@@ -2793,37 +3054,6 @@ function parseTypst(typst) {
|
|
|
2793
3054
|
}
|
|
2794
3055
|
|
|
2795
3056
|
// src/tex-writer.ts
|
|
2796
|
-
var TYPST_UNARY_FUNCTIONS = [
|
|
2797
|
-
"sqrt",
|
|
2798
|
-
"bold",
|
|
2799
|
-
"arrow",
|
|
2800
|
-
"upright",
|
|
2801
|
-
"lr",
|
|
2802
|
-
"op",
|
|
2803
|
-
"macron",
|
|
2804
|
-
"dot",
|
|
2805
|
-
"dot.double",
|
|
2806
|
-
"hat",
|
|
2807
|
-
"tilde",
|
|
2808
|
-
"overline",
|
|
2809
|
-
"underline",
|
|
2810
|
-
"bb",
|
|
2811
|
-
"cal",
|
|
2812
|
-
"frak"
|
|
2813
|
-
];
|
|
2814
|
-
var TYPST_BINARY_FUNCTIONS = [
|
|
2815
|
-
"frac",
|
|
2816
|
-
"root",
|
|
2817
|
-
"overbrace",
|
|
2818
|
-
"underbrace"
|
|
2819
|
-
];
|
|
2820
|
-
function apply_escape_if_needed2(c) {
|
|
2821
|
-
if (["{", "}", "%"].includes(c)) {
|
|
2822
|
-
return "\\" + c;
|
|
2823
|
-
}
|
|
2824
|
-
return c;
|
|
2825
|
-
}
|
|
2826
|
-
|
|
2827
3057
|
class TexWriter {
|
|
2828
3058
|
buffer = "";
|
|
2829
3059
|
queue = [];
|
|
@@ -2875,180 +3105,6 @@ class TexWriter {
|
|
|
2875
3105
|
return this.buffer;
|
|
2876
3106
|
}
|
|
2877
3107
|
}
|
|
2878
|
-
function convert_typst_node_to_tex(node) {
|
|
2879
|
-
if (node.eq(new TypstNode("symbol", "eq.def"))) {
|
|
2880
|
-
return new TexNode("binaryFunc", "\\overset", [
|
|
2881
|
-
new TexNode("text", "def"),
|
|
2882
|
-
new TexNode("element", "=")
|
|
2883
|
-
]);
|
|
2884
|
-
}
|
|
2885
|
-
switch (node.type) {
|
|
2886
|
-
case "empty":
|
|
2887
|
-
return new TexNode("empty", "");
|
|
2888
|
-
case "whitespace":
|
|
2889
|
-
return new TexNode("whitespace", node.content);
|
|
2890
|
-
case "atom":
|
|
2891
|
-
return new TexNode("element", node.content);
|
|
2892
|
-
case "symbol":
|
|
2893
|
-
switch (node.content) {
|
|
2894
|
-
case "comma":
|
|
2895
|
-
return new TexNode("element", ",");
|
|
2896
|
-
case "hyph":
|
|
2897
|
-
case "hyph.minus":
|
|
2898
|
-
return new TexNode("text", "-");
|
|
2899
|
-
default:
|
|
2900
|
-
return new TexNode("symbol", typst_token_to_tex(node.content));
|
|
2901
|
-
}
|
|
2902
|
-
case "text":
|
|
2903
|
-
return new TexNode("text", node.content);
|
|
2904
|
-
case "comment":
|
|
2905
|
-
return new TexNode("comment", node.content);
|
|
2906
|
-
case "group": {
|
|
2907
|
-
const args = node.args.map(convert_typst_node_to_tex);
|
|
2908
|
-
if (node.content === "parenthesis") {
|
|
2909
|
-
args.unshift(new TexNode("element", "("));
|
|
2910
|
-
args.push(new TexNode("element", ")"));
|
|
2911
|
-
}
|
|
2912
|
-
return new TexNode("ordgroup", "", args);
|
|
2913
|
-
}
|
|
2914
|
-
case "funcCall": {
|
|
2915
|
-
if (TYPST_UNARY_FUNCTIONS.includes(node.content)) {
|
|
2916
|
-
if (node.content === "lr") {
|
|
2917
|
-
const body = node.args[0];
|
|
2918
|
-
if (body.type === "group") {
|
|
2919
|
-
let left_delim = body.args[0].content;
|
|
2920
|
-
let right_delim = body.args[body.args.length - 1].content;
|
|
2921
|
-
left_delim = apply_escape_if_needed2(left_delim);
|
|
2922
|
-
right_delim = apply_escape_if_needed2(right_delim);
|
|
2923
|
-
return new TexNode("ordgroup", "", [
|
|
2924
|
-
new TexNode("element", "\\left" + left_delim),
|
|
2925
|
-
...body.args.slice(1, body.args.length - 1).map(convert_typst_node_to_tex),
|
|
2926
|
-
new TexNode("element", "\\right" + right_delim)
|
|
2927
|
-
]);
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
const command = typst_token_to_tex(node.content);
|
|
2931
|
-
return new TexNode("unaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2932
|
-
} else if (TYPST_BINARY_FUNCTIONS.includes(node.content)) {
|
|
2933
|
-
if (node.content === "root") {
|
|
2934
|
-
const [degree, radicand] = node.args;
|
|
2935
|
-
const data = convert_typst_node_to_tex(degree);
|
|
2936
|
-
return new TexNode("unaryFunc", "\\sqrt", [convert_typst_node_to_tex(radicand)], data);
|
|
2937
|
-
}
|
|
2938
|
-
if (node.content === "overbrace" || node.content === "underbrace") {
|
|
2939
|
-
const [body, label] = node.args;
|
|
2940
|
-
const base = new TexNode("unaryFunc", "\\" + node.content, [convert_typst_node_to_tex(body)]);
|
|
2941
|
-
const script = convert_typst_node_to_tex(label);
|
|
2942
|
-
const data = node.content === "overbrace" ? { base, sup: script } : { base, sub: script };
|
|
2943
|
-
return new TexNode("supsub", "", [], data);
|
|
2944
|
-
}
|
|
2945
|
-
const command = typst_token_to_tex(node.content);
|
|
2946
|
-
return new TexNode("binaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
2947
|
-
} else {
|
|
2948
|
-
return new TexNode("ordgroup", "", [
|
|
2949
|
-
new TexNode("symbol", typst_token_to_tex(node.content)),
|
|
2950
|
-
new TexNode("element", "("),
|
|
2951
|
-
...node.args.map(convert_typst_node_to_tex),
|
|
2952
|
-
new TexNode("element", ")")
|
|
2953
|
-
]);
|
|
2954
|
-
}
|
|
2955
|
-
}
|
|
2956
|
-
case "supsub": {
|
|
2957
|
-
const { base, sup, sub } = node.data;
|
|
2958
|
-
const base_tex = convert_typst_node_to_tex(base);
|
|
2959
|
-
let sup_tex;
|
|
2960
|
-
let sub_tex;
|
|
2961
|
-
if (sup) {
|
|
2962
|
-
sup_tex = convert_typst_node_to_tex(sup);
|
|
2963
|
-
}
|
|
2964
|
-
if (sub) {
|
|
2965
|
-
sub_tex = convert_typst_node_to_tex(sub);
|
|
2966
|
-
}
|
|
2967
|
-
const res = new TexNode("supsub", "", [], {
|
|
2968
|
-
base: base_tex,
|
|
2969
|
-
sup: sup_tex,
|
|
2970
|
-
sub: sub_tex
|
|
2971
|
-
});
|
|
2972
|
-
return res;
|
|
2973
|
-
}
|
|
2974
|
-
case "matrix": {
|
|
2975
|
-
const typst_data = node.data;
|
|
2976
|
-
const tex_data = typst_data.map((row) => row.map(convert_typst_node_to_tex));
|
|
2977
|
-
const matrix = new TexNode("beginend", "matrix", [], tex_data);
|
|
2978
|
-
let left_delim = "\\left(";
|
|
2979
|
-
let right_delim = "\\right)";
|
|
2980
|
-
if (node.options) {
|
|
2981
|
-
if ("delim" in node.options) {
|
|
2982
|
-
switch (node.options.delim) {
|
|
2983
|
-
case "#none":
|
|
2984
|
-
return matrix;
|
|
2985
|
-
case "[":
|
|
2986
|
-
left_delim = "\\left[";
|
|
2987
|
-
right_delim = "\\right]";
|
|
2988
|
-
break;
|
|
2989
|
-
case "]":
|
|
2990
|
-
left_delim = "\\left]";
|
|
2991
|
-
right_delim = "\\right[";
|
|
2992
|
-
break;
|
|
2993
|
-
case "{":
|
|
2994
|
-
left_delim = "\\left\\{";
|
|
2995
|
-
right_delim = "\\right\\}";
|
|
2996
|
-
break;
|
|
2997
|
-
case "}":
|
|
2998
|
-
left_delim = "\\left\\}";
|
|
2999
|
-
right_delim = "\\right\\{";
|
|
3000
|
-
break;
|
|
3001
|
-
case "|":
|
|
3002
|
-
left_delim = "\\left|";
|
|
3003
|
-
right_delim = "\\right|";
|
|
3004
|
-
break;
|
|
3005
|
-
case ")":
|
|
3006
|
-
left_delim = "\\left)";
|
|
3007
|
-
right_delim = "\\right(";
|
|
3008
|
-
case "(":
|
|
3009
|
-
default:
|
|
3010
|
-
left_delim = "\\left(";
|
|
3011
|
-
right_delim = "\\right)";
|
|
3012
|
-
break;
|
|
3013
|
-
}
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
return new TexNode("ordgroup", "", [
|
|
3017
|
-
new TexNode("element", left_delim),
|
|
3018
|
-
matrix,
|
|
3019
|
-
new TexNode("element", right_delim)
|
|
3020
|
-
]);
|
|
3021
|
-
}
|
|
3022
|
-
case "control": {
|
|
3023
|
-
switch (node.content) {
|
|
3024
|
-
case "\\":
|
|
3025
|
-
return new TexNode("control", "\\\\");
|
|
3026
|
-
case "&":
|
|
3027
|
-
return new TexNode("control", "&");
|
|
3028
|
-
default:
|
|
3029
|
-
throw new Error("[convert_typst_node_to_tex] Unimplemented control: " + node.content);
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
|
-
case "fraction": {
|
|
3033
|
-
const [numerator, denominator] = node.args;
|
|
3034
|
-
const num_tex = convert_typst_node_to_tex(numerator);
|
|
3035
|
-
const den_tex = convert_typst_node_to_tex(denominator);
|
|
3036
|
-
return new TexNode("binaryFunc", "\\frac", [num_tex, den_tex]);
|
|
3037
|
-
}
|
|
3038
|
-
default:
|
|
3039
|
-
throw new Error("[convert_typst_node_to_tex] Unimplemented type: " + node.type);
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
function typst_token_to_tex(token) {
|
|
3043
|
-
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
3044
|
-
return token;
|
|
3045
|
-
} else if (token === "thin") {
|
|
3046
|
-
return "\\,";
|
|
3047
|
-
} else if (reverseSymbolMap.has(token)) {
|
|
3048
|
-
return "\\" + reverseSymbolMap.get(token);
|
|
3049
|
-
}
|
|
3050
|
-
return "\\" + token;
|
|
3051
|
-
}
|
|
3052
3108
|
|
|
3053
3109
|
// src/index.ts
|
|
3054
3110
|
function tex2typst(tex, options) {
|
|
@@ -3056,6 +3112,7 @@ function tex2typst(tex, options) {
|
|
|
3056
3112
|
nonStrict: true,
|
|
3057
3113
|
preferTypstIntrinsic: true,
|
|
3058
3114
|
keepSpaces: false,
|
|
3115
|
+
fracToSlash: true,
|
|
3059
3116
|
customTexMacros: {}
|
|
3060
3117
|
};
|
|
3061
3118
|
if (options) {
|
|
@@ -3070,7 +3127,7 @@ function tex2typst(tex, options) {
|
|
|
3070
3127
|
}
|
|
3071
3128
|
}
|
|
3072
3129
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
3073
|
-
const typstTree =
|
|
3130
|
+
const typstTree = convert_tex_node_to_typst(texTree, opt);
|
|
3074
3131
|
const writer = new TypstWriter(opt.nonStrict, opt.preferTypstIntrinsic, opt.keepSpaces);
|
|
3075
3132
|
writer.serialize(typstTree);
|
|
3076
3133
|
return writer.finalize();
|