tex2typst 0.3.28 → 0.4.0
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.d.ts +13 -9
- package/dist/index.js +86 -35
- package/dist/tex2typst.min.js +13 -13
- package/package.json +1 -1
- package/src/convert.ts +51 -23
- package/src/exposed-types.ts +13 -9
- package/src/index.ts +23 -12
- package/src/map.ts +1 -0
- package/src/tex-types.ts +2 -0
- package/src/tex-writer.ts +10 -1
- package/src/typst-types.ts +4 -0
- package/tests/struct-bidirection.yaml +16 -1
- package/tests/struct-tex2typst.yaml +0 -12
- package/tests/tex-to-typst.test.ts +1 -1
- package/tests/typst-to-tex.test.ts +36 -12
package/dist/index.d.ts
CHANGED
|
@@ -6,17 +6,21 @@
|
|
|
6
6
|
* Any undocumented options may be not working at present or break in the future!
|
|
7
7
|
*/
|
|
8
8
|
export interface Tex2TypstOptions {
|
|
9
|
-
nonStrict
|
|
10
|
-
preferShorthands
|
|
11
|
-
keepSpaces
|
|
12
|
-
fracToSlash
|
|
13
|
-
inftyToOo
|
|
14
|
-
optimize
|
|
15
|
-
customTexMacros
|
|
9
|
+
nonStrict: boolean; /** default is true */
|
|
10
|
+
preferShorthands: boolean; /** default is true */
|
|
11
|
+
keepSpaces: boolean; /** default is false */
|
|
12
|
+
fracToSlash: boolean; /** default is true */
|
|
13
|
+
inftyToOo: boolean; /** default is false */
|
|
14
|
+
optimize: boolean; /** default is true */
|
|
15
|
+
customTexMacros: { [key: string]: string; };
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export
|
|
19
|
-
|
|
18
|
+
export interface Typst2TexOptions {
|
|
19
|
+
blockMathMode: boolean; /** default is true */
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare function tex2typst(tex: string, options?: Partial<Tex2TypstOptions>): string;
|
|
23
|
+
export declare function typst2tex(typst: string, options?: Partial<Typst2TexOptions>): string;
|
|
20
24
|
|
|
21
25
|
export declare const symbolMap: Map<string, string>;
|
|
22
26
|
export declare const shorthandMap: Map<string, string>;
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,8 @@ var TexToken = class _TexToken {
|
|
|
31
31
|
return new TexTerminal(this);
|
|
32
32
|
}
|
|
33
33
|
static EMPTY = new _TexToken(0 /* EMPTY */, "");
|
|
34
|
+
static COMMAND_DISPLAYSTYLE = new _TexToken(2 /* COMMAND */, "\\displaystyle");
|
|
35
|
+
static COMMAND_TEXTSTYLE = new _TexToken(2 /* COMMAND */, "\\textstyle");
|
|
34
36
|
};
|
|
35
37
|
var TexNode = class {
|
|
36
38
|
type;
|
|
@@ -1229,14 +1231,18 @@ var TypstToken = class _TypstToken {
|
|
|
1229
1231
|
new _TypstToken(2 /* ELEMENT */, "["),
|
|
1230
1232
|
new _TypstToken(2 /* ELEMENT */, "{"),
|
|
1231
1233
|
new _TypstToken(2 /* ELEMENT */, "|"),
|
|
1232
|
-
new _TypstToken(1 /* SYMBOL */, "angle.l")
|
|
1234
|
+
new _TypstToken(1 /* SYMBOL */, "angle.l"),
|
|
1235
|
+
new _TypstToken(1 /* SYMBOL */, "paren.l"),
|
|
1236
|
+
new _TypstToken(1 /* SYMBOL */, "brace.l")
|
|
1233
1237
|
];
|
|
1234
1238
|
static RIGHT_DELIMITERS = [
|
|
1235
1239
|
new _TypstToken(2 /* ELEMENT */, ")"),
|
|
1236
1240
|
new _TypstToken(2 /* ELEMENT */, "]"),
|
|
1237
1241
|
new _TypstToken(2 /* ELEMENT */, "}"),
|
|
1238
1242
|
new _TypstToken(2 /* ELEMENT */, "|"),
|
|
1239
|
-
new _TypstToken(1 /* SYMBOL */, "angle.r")
|
|
1243
|
+
new _TypstToken(1 /* SYMBOL */, "angle.r"),
|
|
1244
|
+
new _TypstToken(1 /* SYMBOL */, "paren.r"),
|
|
1245
|
+
new _TypstToken(1 /* SYMBOL */, "brace.r")
|
|
1240
1246
|
];
|
|
1241
1247
|
};
|
|
1242
1248
|
var TypstWriterError = class extends Error {
|
|
@@ -1655,6 +1661,7 @@ var TypstWriter = class {
|
|
|
1655
1661
|
// src/map.ts
|
|
1656
1662
|
var symbolMap = /* @__PURE__ */ new Map([
|
|
1657
1663
|
["displaystyle", "display"],
|
|
1664
|
+
["textstyle", "inline"],
|
|
1658
1665
|
["hspace", "#h"],
|
|
1659
1666
|
["|", "bar.v.double"],
|
|
1660
1667
|
[",", "thin"],
|
|
@@ -2909,7 +2916,7 @@ function appendWithBracketsIfNeeded(node) {
|
|
|
2909
2916
|
return node;
|
|
2910
2917
|
}
|
|
2911
2918
|
}
|
|
2912
|
-
function convert_tex_node_to_typst(abstractNode, options
|
|
2919
|
+
function convert_tex_node_to_typst(abstractNode, options) {
|
|
2913
2920
|
switch (abstractNode.type) {
|
|
2914
2921
|
case "terminal": {
|
|
2915
2922
|
const node2 = abstractNode;
|
|
@@ -3221,7 +3228,8 @@ function typst_token_to_tex(token) {
|
|
|
3221
3228
|
}
|
|
3222
3229
|
}
|
|
3223
3230
|
var TEX_NODE_COMMA = new TexToken(1 /* ELEMENT */, ",").toNode();
|
|
3224
|
-
function convert_typst_node_to_tex(abstractNode) {
|
|
3231
|
+
function convert_typst_node_to_tex(abstractNode, options) {
|
|
3232
|
+
const convert_node = (node) => convert_typst_node_to_tex(node, options);
|
|
3225
3233
|
switch (abstractNode.type) {
|
|
3226
3234
|
case "terminal": {
|
|
3227
3235
|
const node = abstractNode;
|
|
@@ -3254,7 +3262,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3254
3262
|
}
|
|
3255
3263
|
case "group": {
|
|
3256
3264
|
const node = abstractNode;
|
|
3257
|
-
const args = node.items.map(
|
|
3265
|
+
const args = node.items.map(convert_node);
|
|
3258
3266
|
const alignment_char = new TexToken(7 /* CONTROL */, "&").toNode();
|
|
3259
3267
|
const newline_char = new TexToken(7 /* CONTROL */, "\\\\").toNode();
|
|
3260
3268
|
if (array_includes(args, alignment_char)) {
|
|
@@ -3270,7 +3278,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3270
3278
|
}
|
|
3271
3279
|
case "leftright": {
|
|
3272
3280
|
const node = abstractNode;
|
|
3273
|
-
const body =
|
|
3281
|
+
const body = convert_node(node.body);
|
|
3274
3282
|
let left = node.left ? typst_token_to_tex(node.left) : new TexToken(1 /* ELEMENT */, ".");
|
|
3275
3283
|
let right = node.right ? typst_token_to_tex(node.right) : new TexToken(1 /* ELEMENT */, ".");
|
|
3276
3284
|
if (node.isOverHigh()) {
|
|
@@ -3287,7 +3295,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3287
3295
|
// `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
|
|
3288
3296
|
case "norm": {
|
|
3289
3297
|
const arg0 = node.args[0];
|
|
3290
|
-
const body =
|
|
3298
|
+
const body = convert_node(arg0);
|
|
3291
3299
|
if (node.isOverHigh()) {
|
|
3292
3300
|
return new TexLeftRight({
|
|
3293
3301
|
body,
|
|
@@ -3308,7 +3316,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3308
3316
|
const left = "\\l" + node.head.value;
|
|
3309
3317
|
const right = "\\r" + node.head.value;
|
|
3310
3318
|
const arg0 = node.args[0];
|
|
3311
|
-
const body =
|
|
3319
|
+
const body = convert_node(arg0);
|
|
3312
3320
|
const left_node = new TexToken(2 /* COMMAND */, left);
|
|
3313
3321
|
const right_node = new TexToken(2 /* COMMAND */, right);
|
|
3314
3322
|
if (node.isOverHigh()) {
|
|
@@ -3324,22 +3332,22 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3324
3332
|
// special hook for root
|
|
3325
3333
|
case "root": {
|
|
3326
3334
|
const [degree, radicand] = node.args;
|
|
3327
|
-
const data =
|
|
3328
|
-
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [
|
|
3335
|
+
const data = convert_node(degree);
|
|
3336
|
+
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [convert_node(radicand)], data);
|
|
3329
3337
|
}
|
|
3330
3338
|
// special hook for overbrace and underbrace
|
|
3331
3339
|
case "overbrace":
|
|
3332
3340
|
case "underbrace": {
|
|
3333
3341
|
const [body, label] = node.args;
|
|
3334
|
-
const base = new TexFuncCall(typst_token_to_tex(node.head), [
|
|
3335
|
-
const script =
|
|
3342
|
+
const base = new TexFuncCall(typst_token_to_tex(node.head), [convert_node(body)]);
|
|
3343
|
+
const script = convert_node(label);
|
|
3336
3344
|
const data = node.head.value === "overbrace" ? { base, sup: script, sub: null } : { base, sub: script, sup: null };
|
|
3337
3345
|
return new TexSupSub(data);
|
|
3338
3346
|
}
|
|
3339
3347
|
// special hook for vec
|
|
3340
3348
|
// "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
|
|
3341
3349
|
case "vec": {
|
|
3342
|
-
const tex_matrix = node.args.map(
|
|
3350
|
+
const tex_matrix = node.args.map((arg) => [convert_node(arg)]);
|
|
3343
3351
|
return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), tex_matrix);
|
|
3344
3352
|
}
|
|
3345
3353
|
// special hook for op
|
|
@@ -3370,20 +3378,46 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3370
3378
|
}
|
|
3371
3379
|
return new TexFuncCall(
|
|
3372
3380
|
new TexToken(2 /* COMMAND */, command),
|
|
3373
|
-
[
|
|
3381
|
+
[convert_node(node.args[1])]
|
|
3374
3382
|
);
|
|
3375
3383
|
}
|
|
3384
|
+
// display(...) -> \displaystyle ... \textstyle
|
|
3385
|
+
// The postprocessor will remove \textstyle if it is the end of the math code
|
|
3386
|
+
case "display": {
|
|
3387
|
+
const arg0 = node.args[0];
|
|
3388
|
+
const group = new TexGroup([
|
|
3389
|
+
TexToken.COMMAND_DISPLAYSTYLE.toNode(),
|
|
3390
|
+
convert_node(arg0)
|
|
3391
|
+
]);
|
|
3392
|
+
if (!options.blockMathMode) {
|
|
3393
|
+
group.items.push(TexToken.COMMAND_TEXTSTYLE.toNode());
|
|
3394
|
+
}
|
|
3395
|
+
return group;
|
|
3396
|
+
}
|
|
3397
|
+
// inline(...) -> \textstyle ... \displaystyle
|
|
3398
|
+
// The postprocessor will remove \displaystyle if it is the end of the math code
|
|
3399
|
+
case "inline": {
|
|
3400
|
+
const arg0 = node.args[0];
|
|
3401
|
+
const group = new TexGroup([
|
|
3402
|
+
TexToken.COMMAND_TEXTSTYLE.toNode(),
|
|
3403
|
+
convert_node(arg0)
|
|
3404
|
+
]);
|
|
3405
|
+
if (options.blockMathMode) {
|
|
3406
|
+
group.items.push(TexToken.COMMAND_DISPLAYSTYLE.toNode());
|
|
3407
|
+
}
|
|
3408
|
+
return group;
|
|
3409
|
+
}
|
|
3376
3410
|
// general case
|
|
3377
3411
|
default: {
|
|
3378
3412
|
const func_name_tex = typst_token_to_tex(node.head);
|
|
3379
3413
|
const is_known_func = TEX_UNARY_COMMANDS.includes(func_name_tex.value.substring(1)) || TEX_BINARY_COMMANDS.includes(func_name_tex.value.substring(1));
|
|
3380
3414
|
if (func_name_tex.value.length > 0 && is_known_func) {
|
|
3381
|
-
return new TexFuncCall(func_name_tex, node.args.map(
|
|
3415
|
+
return new TexFuncCall(func_name_tex, node.args.map(convert_node));
|
|
3382
3416
|
} else {
|
|
3383
3417
|
return new TexGroup([
|
|
3384
3418
|
typst_token_to_tex(node.head).toNode(),
|
|
3385
3419
|
new TexToken(1 /* ELEMENT */, "(").toNode(),
|
|
3386
|
-
...array_intersperse(node.args.map(
|
|
3420
|
+
...array_intersperse(node.args.map(convert_node), TEX_NODE_COMMA),
|
|
3387
3421
|
new TexToken(1 /* ELEMENT */, ")").toNode()
|
|
3388
3422
|
]);
|
|
3389
3423
|
}
|
|
@@ -3398,7 +3432,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3398
3432
|
const color = node.options["fill"];
|
|
3399
3433
|
return new TexFuncCall(
|
|
3400
3434
|
new TexToken(2 /* COMMAND */, "\\textcolor"),
|
|
3401
|
-
[
|
|
3435
|
+
[convert_node(color), convert_node(node.fragments[0])]
|
|
3402
3436
|
);
|
|
3403
3437
|
}
|
|
3404
3438
|
}
|
|
@@ -3410,11 +3444,11 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3410
3444
|
case "supsub": {
|
|
3411
3445
|
const node = abstractNode;
|
|
3412
3446
|
const { base, sup, sub } = node;
|
|
3413
|
-
const sup_tex = sup ?
|
|
3414
|
-
const sub_tex = sub ?
|
|
3447
|
+
const sup_tex = sup ? convert_node(sup) : null;
|
|
3448
|
+
const sub_tex = sub ? convert_node(sub) : null;
|
|
3415
3449
|
if (base.head.eq(new TypstToken(1 /* SYMBOL */, "limits"))) {
|
|
3416
3450
|
const limits = base;
|
|
3417
|
-
const body_in_limits =
|
|
3451
|
+
const body_in_limits = convert_node(limits.args[0]);
|
|
3418
3452
|
if (sup_tex !== null && sub_tex === null) {
|
|
3419
3453
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, body_in_limits]);
|
|
3420
3454
|
} else if (sup_tex === null && sub_tex !== null) {
|
|
@@ -3424,7 +3458,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3424
3458
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, underset_call]);
|
|
3425
3459
|
}
|
|
3426
3460
|
}
|
|
3427
|
-
const base_tex =
|
|
3461
|
+
const base_tex = convert_node(base);
|
|
3428
3462
|
const res = new TexSupSub({
|
|
3429
3463
|
base: base_tex,
|
|
3430
3464
|
sup: sup_tex,
|
|
@@ -3434,7 +3468,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3434
3468
|
}
|
|
3435
3469
|
case "matrixLike": {
|
|
3436
3470
|
const node = abstractNode;
|
|
3437
|
-
const tex_matrix = node.matrix.map((row) => row.map(
|
|
3471
|
+
const tex_matrix = node.matrix.map((row) => row.map(convert_node));
|
|
3438
3472
|
if (node.head.eq(TypstMatrixLike.MAT)) {
|
|
3439
3473
|
let env_type = "pmatrix";
|
|
3440
3474
|
if (node.options) {
|
|
@@ -3481,8 +3515,8 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3481
3515
|
case "fraction": {
|
|
3482
3516
|
const node = abstractNode;
|
|
3483
3517
|
const [numerator, denominator] = node.args;
|
|
3484
|
-
const num_tex =
|
|
3485
|
-
const den_tex =
|
|
3518
|
+
const num_tex = convert_node(numerator);
|
|
3519
|
+
const den_tex = convert_node(denominator);
|
|
3486
3520
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\frac"), [num_tex, den_tex]);
|
|
3487
3521
|
}
|
|
3488
3522
|
default:
|
|
@@ -3989,6 +4023,14 @@ var TexWriter = class {
|
|
|
3989
4023
|
this.queue = this.queue.concat(node.serialize());
|
|
3990
4024
|
}
|
|
3991
4025
|
flushQueue() {
|
|
4026
|
+
while (this.queue.length > 0) {
|
|
4027
|
+
const last_token = this.queue[this.queue.length - 1];
|
|
4028
|
+
if (last_token.eq(TexToken.COMMAND_DISPLAYSTYLE) || last_token.eq(TexToken.COMMAND_TEXTSTYLE)) {
|
|
4029
|
+
this.queue.pop();
|
|
4030
|
+
} else {
|
|
4031
|
+
break;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
3992
4034
|
for (let i = 0; i < this.queue.length; i++) {
|
|
3993
4035
|
this.buffer = writeTexTokenBuffer(this.buffer, this.queue[i]);
|
|
3994
4036
|
}
|
|
@@ -4001,7 +4043,7 @@ var TexWriter = class {
|
|
|
4001
4043
|
};
|
|
4002
4044
|
|
|
4003
4045
|
// src/index.ts
|
|
4004
|
-
function tex2typst(tex, options) {
|
|
4046
|
+
function tex2typst(tex, options = {}) {
|
|
4005
4047
|
const opt = {
|
|
4006
4048
|
nonStrict: true,
|
|
4007
4049
|
preferShorthands: true,
|
|
@@ -4011,14 +4053,12 @@ function tex2typst(tex, options) {
|
|
|
4011
4053
|
optimize: true,
|
|
4012
4054
|
customTexMacros: {}
|
|
4013
4055
|
};
|
|
4014
|
-
if (options !==
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
opt[key] = options[key];
|
|
4021
|
-
}
|
|
4056
|
+
if (typeof options !== "object") {
|
|
4057
|
+
throw new Error("options must be an object");
|
|
4058
|
+
}
|
|
4059
|
+
for (const key in opt) {
|
|
4060
|
+
if (key in options) {
|
|
4061
|
+
opt[key] = options[key];
|
|
4022
4062
|
}
|
|
4023
4063
|
}
|
|
4024
4064
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
@@ -4027,9 +4067,20 @@ function tex2typst(tex, options) {
|
|
|
4027
4067
|
writer.serialize(typstTree);
|
|
4028
4068
|
return writer.finalize();
|
|
4029
4069
|
}
|
|
4030
|
-
function typst2tex(typst) {
|
|
4070
|
+
function typst2tex(typst, options = {}) {
|
|
4071
|
+
const opt = {
|
|
4072
|
+
blockMathMode: true
|
|
4073
|
+
};
|
|
4074
|
+
if (typeof options !== "object") {
|
|
4075
|
+
throw new Error("options must be an object");
|
|
4076
|
+
}
|
|
4077
|
+
for (const key in opt) {
|
|
4078
|
+
if (key in options) {
|
|
4079
|
+
opt[key] = options[key];
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4031
4082
|
const typstTree = parseTypst(typst);
|
|
4032
|
-
const texTree = convert_typst_node_to_tex(typstTree);
|
|
4083
|
+
const texTree = convert_typst_node_to_tex(typstTree, opt);
|
|
4033
4084
|
const writer = new TexWriter();
|
|
4034
4085
|
writer.append(texTree);
|
|
4035
4086
|
return writer.finalize();
|