tex2typst 0.3.29 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +13 -9
- package/dist/index.js +233 -224
- package/dist/tex2typst.min.js +12 -13
- package/package.json +2 -2
- package/src/convert.ts +88 -36
- package/src/exposed-types.ts +13 -9
- package/src/generic.ts +6 -6
- package/src/index.ts +23 -12
- package/src/map.ts +1 -0
- package/src/tex-parser.ts +19 -47
- package/src/tex-tokenizer.ts +1 -0
- package/src/tex-types.ts +3 -1
- package/src/tex-writer.ts +10 -1
- package/src/typst-parser.ts +114 -160
- package/tests/cheat-sheet.test.ts +0 -42
- package/tests/cheat-sheet.toml +0 -304
- package/tests/example.ts +0 -15
- package/tests/general-symbols.test.ts +0 -22
- package/tests/general-symbols.toml +0 -755
- package/tests/integration-tex2typst.yaml +0 -89
- package/tests/struct-bidirection.yaml +0 -194
- package/tests/struct-tex2typst.yaml +0 -457
- package/tests/struct-typst2tex.yaml +0 -412
- package/tests/symbol.yml +0 -126
- package/tests/test-common.ts +0 -26
- package/tests/tex-parser.test.ts +0 -97
- package/tests/tex-to-typst.test.ts +0 -136
- package/tests/typst-parser.test.ts +0 -134
- package/tests/typst-to-tex.test.ts +0 -76
- /package/src/{util.ts → utils.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/utils.ts
|
|
2
2
|
function isalpha(char) {
|
|
3
3
|
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(char);
|
|
4
4
|
}
|
|
@@ -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;
|
|
@@ -572,7 +574,8 @@ var TEX_UNARY_COMMANDS = [
|
|
|
572
574
|
"mathinner",
|
|
573
575
|
"mathrel",
|
|
574
576
|
"mathbin",
|
|
575
|
-
"mathop"
|
|
577
|
+
"mathop",
|
|
578
|
+
"not"
|
|
576
579
|
];
|
|
577
580
|
var TEX_BINARY_COMMANDS = [
|
|
578
581
|
"frac",
|
|
@@ -1064,51 +1067,25 @@ var LatexParser = class {
|
|
|
1064
1067
|
this.alignmentDepth++;
|
|
1065
1068
|
let pos = start;
|
|
1066
1069
|
pos += eat_whitespaces(tokens, pos).length;
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
let group = new TexGroup([]);
|
|
1071
|
-
row.push(group);
|
|
1072
|
-
while (pos < tokens.length) {
|
|
1073
|
-
if (tokens[pos].eq(closingToken)) {
|
|
1074
|
-
break;
|
|
1075
|
-
}
|
|
1076
|
-
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
1077
|
-
pos = newPos;
|
|
1078
|
-
if (res.head.type === 5 /* SPACE */ || res.head.type === 6 /* NEWLINE */) {
|
|
1079
|
-
if (!this.space_sensitive && res.head.value.replace(/ /g, "").length === 0) {
|
|
1080
|
-
continue;
|
|
1081
|
-
}
|
|
1082
|
-
if (!this.newline_sensitive && res.head.value === "\n") {
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
if (res.head.eq(new TexToken(7 /* CONTROL */, "\\\\"))) {
|
|
1087
|
-
row = [];
|
|
1088
|
-
group = new TexGroup([]);
|
|
1089
|
-
row.push(group);
|
|
1090
|
-
allRows.push(row);
|
|
1091
|
-
} else if (res.head.eq(new TexToken(7 /* CONTROL */, "&"))) {
|
|
1092
|
-
group = new TexGroup([]);
|
|
1093
|
-
row.push(group);
|
|
1094
|
-
} else {
|
|
1095
|
-
group.items.push(res);
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
if (pos >= tokens.length) {
|
|
1070
|
+
let closure;
|
|
1071
|
+
[closure, pos] = this.parseClosure(tokens, pos, closingToken);
|
|
1072
|
+
if (pos === -1) {
|
|
1099
1073
|
return [[], -1];
|
|
1100
1074
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
last_cell_items.pop();
|
|
1107
|
-
}
|
|
1075
|
+
let allRows;
|
|
1076
|
+
if (closure.type === "ordgroup") {
|
|
1077
|
+
const elements = closure.items;
|
|
1078
|
+
while (elements.length > 0 && [5 /* SPACE */, 6 /* NEWLINE */].includes(elements[elements.length - 1].head.type)) {
|
|
1079
|
+
elements.pop();
|
|
1108
1080
|
}
|
|
1081
|
+
allRows = array_split(elements, new TexToken(7 /* CONTROL */, "\\\\").toNode()).map((row) => {
|
|
1082
|
+
return array_split(row, new TexToken(7 /* CONTROL */, "&").toNode()).map((arr) => new TexGroup(arr));
|
|
1083
|
+
});
|
|
1084
|
+
} else {
|
|
1085
|
+
allRows = [[closure]];
|
|
1109
1086
|
}
|
|
1110
1087
|
this.alignmentDepth--;
|
|
1111
|
-
return [allRows, pos
|
|
1088
|
+
return [allRows, pos];
|
|
1112
1089
|
}
|
|
1113
1090
|
};
|
|
1114
1091
|
function passIgnoreWhitespaceBeforeScriptMark(tokens) {
|
|
@@ -1659,6 +1636,7 @@ var TypstWriter = class {
|
|
|
1659
1636
|
// src/map.ts
|
|
1660
1637
|
var symbolMap = /* @__PURE__ */ new Map([
|
|
1661
1638
|
["displaystyle", "display"],
|
|
1639
|
+
["textstyle", "inline"],
|
|
1662
1640
|
["hspace", "#h"],
|
|
1663
1641
|
["|", "bar.v.double"],
|
|
1664
1642
|
[",", "thin"],
|
|
@@ -2913,7 +2891,7 @@ function appendWithBracketsIfNeeded(node) {
|
|
|
2913
2891
|
return node;
|
|
2914
2892
|
}
|
|
2915
2893
|
}
|
|
2916
|
-
function convert_tex_node_to_typst(abstractNode, options
|
|
2894
|
+
function convert_tex_node_to_typst(abstractNode, options) {
|
|
2917
2895
|
switch (abstractNode.type) {
|
|
2918
2896
|
case "terminal": {
|
|
2919
2897
|
const node2 = abstractNode;
|
|
@@ -3080,6 +3058,20 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
|
|
|
3080
3058
|
{ body: arg0, left: TypstToken.LEFT_BRACE, right: TypstToken.RIGHT_BRACE }
|
|
3081
3059
|
);
|
|
3082
3060
|
}
|
|
3061
|
+
if (node2.head.value === "\\not") {
|
|
3062
|
+
const sym = convert_tex_node_to_typst(node2.args[0], options);
|
|
3063
|
+
assert(sym.type === "terminal");
|
|
3064
|
+
if (sym.head.type === 1 /* SYMBOL */) {
|
|
3065
|
+
return new TypstToken(1 /* SYMBOL */, sym.head.value + ".not").toNode();
|
|
3066
|
+
} else {
|
|
3067
|
+
switch (sym.head.value) {
|
|
3068
|
+
case "=":
|
|
3069
|
+
return new TypstToken(1 /* SYMBOL */, "eq.not").toNode();
|
|
3070
|
+
default:
|
|
3071
|
+
throw new Error(`Not supported: \\not ${sym.head.value}`);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3083
3075
|
if (node2.head.value === "\\overset") {
|
|
3084
3076
|
return convert_overset(node2, options);
|
|
3085
3077
|
}
|
|
@@ -3179,12 +3171,28 @@ function typst_token_to_tex(token) {
|
|
|
3179
3171
|
return TexToken.EMPTY;
|
|
3180
3172
|
case 1 /* SYMBOL */: {
|
|
3181
3173
|
const _typst_symbol_to_tex = function(symbol) {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3174
|
+
switch (symbol) {
|
|
3175
|
+
case "eq":
|
|
3176
|
+
return "=";
|
|
3177
|
+
case "plus":
|
|
3178
|
+
return "+";
|
|
3179
|
+
case "minus":
|
|
3180
|
+
return "-";
|
|
3181
|
+
case "percent":
|
|
3182
|
+
return "%";
|
|
3183
|
+
default: {
|
|
3184
|
+
if (reverseSymbolMap.has(symbol)) {
|
|
3185
|
+
return "\\" + reverseSymbolMap.get(symbol);
|
|
3186
|
+
} else {
|
|
3187
|
+
return "\\" + symbol;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3186
3190
|
}
|
|
3187
3191
|
};
|
|
3192
|
+
if (token.value.endsWith(".not")) {
|
|
3193
|
+
const sym = _typst_symbol_to_tex(token.value.slice(0, -4));
|
|
3194
|
+
return new TexToken(2 /* COMMAND */, sym.startsWith("\\") ? `\\not${sym}` : `\\not ${sym}`);
|
|
3195
|
+
}
|
|
3188
3196
|
return new TexToken(2 /* COMMAND */, _typst_symbol_to_tex(token.value));
|
|
3189
3197
|
}
|
|
3190
3198
|
case 2 /* ELEMENT */: {
|
|
@@ -3225,7 +3233,8 @@ function typst_token_to_tex(token) {
|
|
|
3225
3233
|
}
|
|
3226
3234
|
}
|
|
3227
3235
|
var TEX_NODE_COMMA = new TexToken(1 /* ELEMENT */, ",").toNode();
|
|
3228
|
-
function convert_typst_node_to_tex(abstractNode) {
|
|
3236
|
+
function convert_typst_node_to_tex(abstractNode, options) {
|
|
3237
|
+
const convert_node = (node) => convert_typst_node_to_tex(node, options);
|
|
3229
3238
|
switch (abstractNode.type) {
|
|
3230
3239
|
case "terminal": {
|
|
3231
3240
|
const node = abstractNode;
|
|
@@ -3258,7 +3267,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3258
3267
|
}
|
|
3259
3268
|
case "group": {
|
|
3260
3269
|
const node = abstractNode;
|
|
3261
|
-
const args = node.items.map(
|
|
3270
|
+
const args = node.items.map(convert_node);
|
|
3262
3271
|
const alignment_char = new TexToken(7 /* CONTROL */, "&").toNode();
|
|
3263
3272
|
const newline_char = new TexToken(7 /* CONTROL */, "\\\\").toNode();
|
|
3264
3273
|
if (array_includes(args, alignment_char)) {
|
|
@@ -3274,7 +3283,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3274
3283
|
}
|
|
3275
3284
|
case "leftright": {
|
|
3276
3285
|
const node = abstractNode;
|
|
3277
|
-
const body =
|
|
3286
|
+
const body = convert_node(node.body);
|
|
3278
3287
|
let left = node.left ? typst_token_to_tex(node.left) : new TexToken(1 /* ELEMENT */, ".");
|
|
3279
3288
|
let right = node.right ? typst_token_to_tex(node.right) : new TexToken(1 /* ELEMENT */, ".");
|
|
3280
3289
|
if (node.isOverHigh()) {
|
|
@@ -3291,7 +3300,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3291
3300
|
// `\left\| a + \frac{1}{3} \right\|` <- `norm(a + 1/3)`
|
|
3292
3301
|
case "norm": {
|
|
3293
3302
|
const arg0 = node.args[0];
|
|
3294
|
-
const body =
|
|
3303
|
+
const body = convert_node(arg0);
|
|
3295
3304
|
if (node.isOverHigh()) {
|
|
3296
3305
|
return new TexLeftRight({
|
|
3297
3306
|
body,
|
|
@@ -3312,7 +3321,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3312
3321
|
const left = "\\l" + node.head.value;
|
|
3313
3322
|
const right = "\\r" + node.head.value;
|
|
3314
3323
|
const arg0 = node.args[0];
|
|
3315
|
-
const body =
|
|
3324
|
+
const body = convert_node(arg0);
|
|
3316
3325
|
const left_node = new TexToken(2 /* COMMAND */, left);
|
|
3317
3326
|
const right_node = new TexToken(2 /* COMMAND */, right);
|
|
3318
3327
|
if (node.isOverHigh()) {
|
|
@@ -3328,22 +3337,22 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3328
3337
|
// special hook for root
|
|
3329
3338
|
case "root": {
|
|
3330
3339
|
const [degree, radicand] = node.args;
|
|
3331
|
-
const data =
|
|
3332
|
-
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [
|
|
3340
|
+
const data = convert_node(degree);
|
|
3341
|
+
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [convert_node(radicand)], data);
|
|
3333
3342
|
}
|
|
3334
3343
|
// special hook for overbrace and underbrace
|
|
3335
3344
|
case "overbrace":
|
|
3336
3345
|
case "underbrace": {
|
|
3337
3346
|
const [body, label] = node.args;
|
|
3338
|
-
const base = new TexFuncCall(typst_token_to_tex(node.head), [
|
|
3339
|
-
const script =
|
|
3347
|
+
const base = new TexFuncCall(typst_token_to_tex(node.head), [convert_node(body)]);
|
|
3348
|
+
const script = convert_node(label);
|
|
3340
3349
|
const data = node.head.value === "overbrace" ? { base, sup: script, sub: null } : { base, sub: script, sup: null };
|
|
3341
3350
|
return new TexSupSub(data);
|
|
3342
3351
|
}
|
|
3343
3352
|
// special hook for vec
|
|
3344
3353
|
// "vec(a, b, c)" -> "\begin{pmatrix}a\\ b\\ c\end{pmatrix}"
|
|
3345
3354
|
case "vec": {
|
|
3346
|
-
const tex_matrix = node.args.map(
|
|
3355
|
+
const tex_matrix = node.args.map((arg) => [convert_node(arg)]);
|
|
3347
3356
|
return new TexBeginEnd(new TexToken(3 /* LITERAL */, "pmatrix"), tex_matrix);
|
|
3348
3357
|
}
|
|
3349
3358
|
// special hook for op
|
|
@@ -3374,20 +3383,46 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3374
3383
|
}
|
|
3375
3384
|
return new TexFuncCall(
|
|
3376
3385
|
new TexToken(2 /* COMMAND */, command),
|
|
3377
|
-
[
|
|
3386
|
+
[convert_node(node.args[1])]
|
|
3378
3387
|
);
|
|
3379
3388
|
}
|
|
3389
|
+
// display(...) -> \displaystyle ... \textstyle
|
|
3390
|
+
// The postprocessor will remove \textstyle if it is the end of the math code
|
|
3391
|
+
case "display": {
|
|
3392
|
+
const arg0 = node.args[0];
|
|
3393
|
+
const group = new TexGroup([
|
|
3394
|
+
TexToken.COMMAND_DISPLAYSTYLE.toNode(),
|
|
3395
|
+
convert_node(arg0)
|
|
3396
|
+
]);
|
|
3397
|
+
if (!options.blockMathMode) {
|
|
3398
|
+
group.items.push(TexToken.COMMAND_TEXTSTYLE.toNode());
|
|
3399
|
+
}
|
|
3400
|
+
return group;
|
|
3401
|
+
}
|
|
3402
|
+
// inline(...) -> \textstyle ... \displaystyle
|
|
3403
|
+
// The postprocessor will remove \displaystyle if it is the end of the math code
|
|
3404
|
+
case "inline": {
|
|
3405
|
+
const arg0 = node.args[0];
|
|
3406
|
+
const group = new TexGroup([
|
|
3407
|
+
TexToken.COMMAND_TEXTSTYLE.toNode(),
|
|
3408
|
+
convert_node(arg0)
|
|
3409
|
+
]);
|
|
3410
|
+
if (options.blockMathMode) {
|
|
3411
|
+
group.items.push(TexToken.COMMAND_DISPLAYSTYLE.toNode());
|
|
3412
|
+
}
|
|
3413
|
+
return group;
|
|
3414
|
+
}
|
|
3380
3415
|
// general case
|
|
3381
3416
|
default: {
|
|
3382
3417
|
const func_name_tex = typst_token_to_tex(node.head);
|
|
3383
3418
|
const is_known_func = TEX_UNARY_COMMANDS.includes(func_name_tex.value.substring(1)) || TEX_BINARY_COMMANDS.includes(func_name_tex.value.substring(1));
|
|
3384
3419
|
if (func_name_tex.value.length > 0 && is_known_func) {
|
|
3385
|
-
return new TexFuncCall(func_name_tex, node.args.map(
|
|
3420
|
+
return new TexFuncCall(func_name_tex, node.args.map(convert_node));
|
|
3386
3421
|
} else {
|
|
3387
3422
|
return new TexGroup([
|
|
3388
3423
|
typst_token_to_tex(node.head).toNode(),
|
|
3389
3424
|
new TexToken(1 /* ELEMENT */, "(").toNode(),
|
|
3390
|
-
...array_intersperse(node.args.map(
|
|
3425
|
+
...array_intersperse(node.args.map(convert_node), TEX_NODE_COMMA),
|
|
3391
3426
|
new TexToken(1 /* ELEMENT */, ")").toNode()
|
|
3392
3427
|
]);
|
|
3393
3428
|
}
|
|
@@ -3402,7 +3437,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3402
3437
|
const color = node.options["fill"];
|
|
3403
3438
|
return new TexFuncCall(
|
|
3404
3439
|
new TexToken(2 /* COMMAND */, "\\textcolor"),
|
|
3405
|
-
[
|
|
3440
|
+
[convert_node(color), convert_node(node.fragments[0])]
|
|
3406
3441
|
);
|
|
3407
3442
|
}
|
|
3408
3443
|
}
|
|
@@ -3414,11 +3449,11 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3414
3449
|
case "supsub": {
|
|
3415
3450
|
const node = abstractNode;
|
|
3416
3451
|
const { base, sup, sub } = node;
|
|
3417
|
-
const sup_tex = sup ?
|
|
3418
|
-
const sub_tex = sub ?
|
|
3452
|
+
const sup_tex = sup ? convert_node(sup) : null;
|
|
3453
|
+
const sub_tex = sub ? convert_node(sub) : null;
|
|
3419
3454
|
if (base.head.eq(new TypstToken(1 /* SYMBOL */, "limits"))) {
|
|
3420
3455
|
const limits = base;
|
|
3421
|
-
const body_in_limits =
|
|
3456
|
+
const body_in_limits = convert_node(limits.args[0]);
|
|
3422
3457
|
if (sup_tex !== null && sub_tex === null) {
|
|
3423
3458
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, body_in_limits]);
|
|
3424
3459
|
} else if (sup_tex === null && sub_tex !== null) {
|
|
@@ -3428,7 +3463,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3428
3463
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\overset"), [sup_tex, underset_call]);
|
|
3429
3464
|
}
|
|
3430
3465
|
}
|
|
3431
|
-
const base_tex =
|
|
3466
|
+
const base_tex = convert_node(base);
|
|
3432
3467
|
const res = new TexSupSub({
|
|
3433
3468
|
base: base_tex,
|
|
3434
3469
|
sup: sup_tex,
|
|
@@ -3438,7 +3473,7 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3438
3473
|
}
|
|
3439
3474
|
case "matrixLike": {
|
|
3440
3475
|
const node = abstractNode;
|
|
3441
|
-
const tex_matrix = node.matrix.map((row) => row.map(
|
|
3476
|
+
const tex_matrix = node.matrix.map((row) => row.map(convert_node));
|
|
3442
3477
|
if (node.head.eq(TypstMatrixLike.MAT)) {
|
|
3443
3478
|
let env_type = "pmatrix";
|
|
3444
3479
|
if (node.options) {
|
|
@@ -3485,8 +3520,8 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3485
3520
|
case "fraction": {
|
|
3486
3521
|
const node = abstractNode;
|
|
3487
3522
|
const [numerator, denominator] = node.args;
|
|
3488
|
-
const num_tex =
|
|
3489
|
-
const den_tex =
|
|
3523
|
+
const num_tex = convert_node(numerator);
|
|
3524
|
+
const den_tex = convert_node(denominator);
|
|
3490
3525
|
return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\frac"), [num_tex, den_tex]);
|
|
3491
3526
|
}
|
|
3492
3527
|
default:
|
|
@@ -3602,32 +3637,34 @@ function find_closing_match(tokens, start) {
|
|
|
3602
3637
|
[RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2]
|
|
3603
3638
|
);
|
|
3604
3639
|
}
|
|
3605
|
-
function
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
}
|
|
3613
|
-
function find_closing_parenthesis(nodes, start) {
|
|
3614
|
-
const left_parenthesis = new TypstToken(2 /* ELEMENT */, "(").toNode();
|
|
3615
|
-
const right_parenthesis = new TypstToken(2 /* ELEMENT */, ")").toNode();
|
|
3616
|
-
assert(nodes[start].eq(left_parenthesis));
|
|
3617
|
-
let count = 1;
|
|
3618
|
-
let pos = start + 1;
|
|
3619
|
-
while (count > 0) {
|
|
3620
|
-
if (pos >= nodes.length) {
|
|
3621
|
-
throw new Error("Unmatched '('");
|
|
3640
|
+
function extract_named_params(arr) {
|
|
3641
|
+
const COLON = new TypstToken(2 /* ELEMENT */, ":").toNode();
|
|
3642
|
+
const np = {};
|
|
3643
|
+
const to_delete = [];
|
|
3644
|
+
for (let i = 0; i < arr.length; i++) {
|
|
3645
|
+
if (arr[i].type !== "group") {
|
|
3646
|
+
continue;
|
|
3622
3647
|
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3648
|
+
const g = arr[i];
|
|
3649
|
+
const pos_colon = array_find(g.items, COLON);
|
|
3650
|
+
if (pos_colon === -1 || pos_colon === 0) {
|
|
3651
|
+
continue;
|
|
3652
|
+
}
|
|
3653
|
+
to_delete.push(i);
|
|
3654
|
+
const param_name = g.items[pos_colon - 1];
|
|
3655
|
+
if (param_name.eq(new TypstToken(1 /* SYMBOL */, "delim").toNode())) {
|
|
3656
|
+
if (g.items.length !== 3) {
|
|
3657
|
+
throw new TypstParserError("Invalid number of arguments for delim");
|
|
3658
|
+
}
|
|
3659
|
+
np["delim"] = g.items[pos_colon + 1];
|
|
3660
|
+
} else {
|
|
3661
|
+
throw new TypstParserError("Not implemented for other named parameters");
|
|
3627
3662
|
}
|
|
3628
|
-
pos += 1;
|
|
3629
3663
|
}
|
|
3630
|
-
|
|
3664
|
+
for (let i = to_delete.length - 1; i >= 0; i--) {
|
|
3665
|
+
arr.splice(to_delete[i], 1);
|
|
3666
|
+
}
|
|
3667
|
+
return [arr, np];
|
|
3631
3668
|
}
|
|
3632
3669
|
function primes(num) {
|
|
3633
3670
|
const res = [];
|
|
@@ -3666,30 +3703,16 @@ function trim_whitespace_around_operators(nodes) {
|
|
|
3666
3703
|
}
|
|
3667
3704
|
return res;
|
|
3668
3705
|
}
|
|
3669
|
-
function process_operators(nodes
|
|
3706
|
+
function process_operators(nodes) {
|
|
3670
3707
|
nodes = trim_whitespace_around_operators(nodes);
|
|
3671
|
-
const opening_bracket = LEFT_PARENTHESES.toNode();
|
|
3672
|
-
const closing_bracket = RIGHT_PARENTHESES.toNode();
|
|
3673
3708
|
const stack = [];
|
|
3674
3709
|
const args = [];
|
|
3675
3710
|
let pos = 0;
|
|
3676
3711
|
while (pos < nodes.length) {
|
|
3677
|
-
const
|
|
3678
|
-
if (
|
|
3679
|
-
|
|
3680
|
-
} else if (current.eq(DIV)) {
|
|
3681
|
-
stack.push(current);
|
|
3682
|
-
pos++;
|
|
3712
|
+
const current_tree = nodes[pos];
|
|
3713
|
+
if (current_tree.eq(DIV)) {
|
|
3714
|
+
stack.push(current_tree);
|
|
3683
3715
|
} else {
|
|
3684
|
-
let current_tree;
|
|
3685
|
-
if (current.eq(opening_bracket)) {
|
|
3686
|
-
const pos_closing = find_closing_parenthesis(nodes, pos);
|
|
3687
|
-
current_tree = process_operators(nodes.slice(pos + 1, pos_closing), true);
|
|
3688
|
-
pos = pos_closing + 1;
|
|
3689
|
-
} else {
|
|
3690
|
-
current_tree = current;
|
|
3691
|
-
pos++;
|
|
3692
|
-
}
|
|
3693
3716
|
if (stack.length > 0 && stack[stack.length - 1].eq(DIV)) {
|
|
3694
3717
|
let denominator = current_tree;
|
|
3695
3718
|
if (args.length === 0) {
|
|
@@ -3708,13 +3731,9 @@ function process_operators(nodes, parenthesis = false) {
|
|
|
3708
3731
|
args.push(current_tree);
|
|
3709
3732
|
}
|
|
3710
3733
|
}
|
|
3734
|
+
pos++;
|
|
3711
3735
|
}
|
|
3712
|
-
|
|
3713
|
-
if (parenthesis) {
|
|
3714
|
-
return new TypstLeftright(null, { body, left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
|
|
3715
|
-
} else {
|
|
3716
|
-
return body;
|
|
3717
|
-
}
|
|
3736
|
+
return args.length === 1 ? args[0] : new TypstGroup(args);
|
|
3718
3737
|
}
|
|
3719
3738
|
function parse_named_params(groups) {
|
|
3720
3739
|
const COLON = new TypstToken(2 /* ELEMENT */, ":").toNode();
|
|
@@ -3742,7 +3761,6 @@ var LEFT_CURLY_BRACKET2 = new TypstToken(2 /* ELEMENT */, "{");
|
|
|
3742
3761
|
var RIGHT_CURLY_BRACKET2 = new TypstToken(2 /* ELEMENT */, "}");
|
|
3743
3762
|
var COMMA = new TypstToken(2 /* ELEMENT */, ",");
|
|
3744
3763
|
var SEMICOLON = new TypstToken(2 /* ELEMENT */, ";");
|
|
3745
|
-
var SINGLE_SPACE = new TypstToken(6 /* SPACE */, " ");
|
|
3746
3764
|
var CONTROL_AND = new TypstToken(7 /* CONTROL */, "&");
|
|
3747
3765
|
var TypstParser = class {
|
|
3748
3766
|
space_sensitive;
|
|
@@ -3755,33 +3773,8 @@ var TypstParser = class {
|
|
|
3755
3773
|
const [tree, _] = this.parseGroup(tokens, 0, tokens.length);
|
|
3756
3774
|
return tree;
|
|
3757
3775
|
}
|
|
3758
|
-
parseGroup(tokens, start, end
|
|
3759
|
-
|
|
3760
|
-
let pos = start;
|
|
3761
|
-
while (pos < end) {
|
|
3762
|
-
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
3763
|
-
pos = newPos;
|
|
3764
|
-
if (res.head.type === 6 /* SPACE */ || res.head.type === 8 /* NEWLINE */) {
|
|
3765
|
-
if (!this.space_sensitive && res.head.value.replace(/ /g, "").length === 0) {
|
|
3766
|
-
continue;
|
|
3767
|
-
}
|
|
3768
|
-
if (!this.newline_sensitive && res.head.value === "\n") {
|
|
3769
|
-
continue;
|
|
3770
|
-
}
|
|
3771
|
-
}
|
|
3772
|
-
results.push(res);
|
|
3773
|
-
}
|
|
3774
|
-
let node;
|
|
3775
|
-
if (parentheses) {
|
|
3776
|
-
node = process_operators(results, true);
|
|
3777
|
-
} else {
|
|
3778
|
-
if (results.length === 1) {
|
|
3779
|
-
node = results[0];
|
|
3780
|
-
} else {
|
|
3781
|
-
node = process_operators(results);
|
|
3782
|
-
}
|
|
3783
|
-
}
|
|
3784
|
-
return [node, end + 1];
|
|
3776
|
+
parseGroup(tokens, start, end) {
|
|
3777
|
+
return this.parseUntil(tokens.slice(start, end), 0, null);
|
|
3785
3778
|
}
|
|
3786
3779
|
parseNextExpr(tokens, start) {
|
|
3787
3780
|
let [base, pos] = this.parseNextExprWithoutSupSub(tokens, start);
|
|
@@ -3810,12 +3803,47 @@ var TypstParser = class {
|
|
|
3810
3803
|
return [base, pos];
|
|
3811
3804
|
}
|
|
3812
3805
|
}
|
|
3806
|
+
// return pos: (position of stopToken) + 1
|
|
3807
|
+
// pos will be -1 if stopToken is not found
|
|
3808
|
+
parseUntil(tokens, start, stopToken, env = {}) {
|
|
3809
|
+
if (env.spaceSensitive === void 0) {
|
|
3810
|
+
env.spaceSensitive = this.space_sensitive;
|
|
3811
|
+
}
|
|
3812
|
+
if (env.newlineSensitive === void 0) {
|
|
3813
|
+
env.newlineSensitive = this.newline_sensitive;
|
|
3814
|
+
}
|
|
3815
|
+
const results = [];
|
|
3816
|
+
let pos = start;
|
|
3817
|
+
while (pos < tokens.length) {
|
|
3818
|
+
if (stopToken !== null && tokens[pos].eq(stopToken)) {
|
|
3819
|
+
break;
|
|
3820
|
+
}
|
|
3821
|
+
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
3822
|
+
pos = newPos;
|
|
3823
|
+
if (res.head.type === 6 /* SPACE */ || res.head.type === 8 /* NEWLINE */) {
|
|
3824
|
+
if (!env.spaceSensitive && res.head.value.replace(/ /g, "").length === 0) {
|
|
3825
|
+
continue;
|
|
3826
|
+
}
|
|
3827
|
+
if (!env.newlineSensitive && res.head.value === "\n") {
|
|
3828
|
+
continue;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
results.push(res);
|
|
3832
|
+
}
|
|
3833
|
+
if (pos >= tokens.length && stopToken !== null) {
|
|
3834
|
+
return [TypstToken.NONE.toNode(), -1];
|
|
3835
|
+
}
|
|
3836
|
+
const node = process_operators(results);
|
|
3837
|
+
return [node, pos + 1];
|
|
3838
|
+
}
|
|
3813
3839
|
parseSupOrSub(tokens, start) {
|
|
3814
3840
|
let node;
|
|
3815
3841
|
let end;
|
|
3816
3842
|
if (tokens[start].eq(LEFT_PARENTHESES)) {
|
|
3817
|
-
|
|
3818
|
-
|
|
3843
|
+
[node, end] = this.parseUntil(tokens, start + 1, RIGHT_PARENTHESES);
|
|
3844
|
+
if (end === -1) {
|
|
3845
|
+
throw new Error("Unmatched '('");
|
|
3846
|
+
}
|
|
3819
3847
|
} else {
|
|
3820
3848
|
[node, end] = this.parseNextExprWithoutSupSub(tokens, start);
|
|
3821
3849
|
}
|
|
@@ -3830,8 +3858,12 @@ var TypstParser = class {
|
|
|
3830
3858
|
const firstToken = tokens[start];
|
|
3831
3859
|
const node = firstToken.toNode();
|
|
3832
3860
|
if (firstToken.eq(LEFT_PARENTHESES)) {
|
|
3833
|
-
const
|
|
3834
|
-
|
|
3861
|
+
const [body, end] = this.parseUntil(tokens, start + 1, RIGHT_PARENTHESES);
|
|
3862
|
+
if (end === -1) {
|
|
3863
|
+
throw new Error("Unmatched '('");
|
|
3864
|
+
}
|
|
3865
|
+
const res = new TypstLeftright(null, { body, left: LEFT_PARENTHESES, right: RIGHT_PARENTHESES });
|
|
3866
|
+
return [res, end];
|
|
3835
3867
|
}
|
|
3836
3868
|
if (firstToken.type === 2 /* ELEMENT */ && !isalpha(firstToken.value[0])) {
|
|
3837
3869
|
return [node, start + 1];
|
|
@@ -3879,70 +3911,41 @@ var TypstParser = class {
|
|
|
3879
3911
|
}
|
|
3880
3912
|
// start: the position of the left parentheses
|
|
3881
3913
|
parseLrArguments(tokens, start) {
|
|
3882
|
-
const lr_token =
|
|
3914
|
+
const lr_token = new TypstToken(1 /* SYMBOL */, "lr");
|
|
3883
3915
|
const end = find_closing_match(tokens, start);
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3916
|
+
let left = null;
|
|
3917
|
+
let right = null;
|
|
3918
|
+
let inner_start = start + 1;
|
|
3919
|
+
let inner_end = end;
|
|
3920
|
+
if (inner_end > inner_start && tokens[inner_start].isOneOf(TypstToken.LEFT_DELIMITERS)) {
|
|
3921
|
+
left = tokens[inner_start];
|
|
3922
|
+
inner_start += 1;
|
|
3923
|
+
}
|
|
3924
|
+
if (inner_end - 1 > inner_start && tokens[inner_end - 1].isOneOf(TypstToken.RIGHT_DELIMITERS)) {
|
|
3925
|
+
right = tokens[inner_end - 1];
|
|
3926
|
+
inner_end -= 1;
|
|
3927
|
+
}
|
|
3928
|
+
const [inner_args, _] = this.parseGroup(tokens, inner_start, inner_end);
|
|
3929
|
+
return [
|
|
3930
|
+
new TypstLeftright(lr_token, { body: inner_args, left, right }),
|
|
3931
|
+
end + 1
|
|
3932
|
+
];
|
|
3899
3933
|
}
|
|
3900
3934
|
// start: the position of the left parentheses
|
|
3901
3935
|
parseMatrix(tokens, start, rowSepToken, cellSepToken) {
|
|
3902
3936
|
const end = find_closing_match(tokens, start);
|
|
3903
|
-
tokens = tokens.slice(0, end);
|
|
3904
3937
|
const matrix = [];
|
|
3905
3938
|
let named_params = {};
|
|
3906
3939
|
let pos = start + 1;
|
|
3907
3940
|
while (pos < end) {
|
|
3908
3941
|
while (pos < end) {
|
|
3909
|
-
let extract_named_params2 = function(arr) {
|
|
3910
|
-
const COLON = new TypstToken(2 /* ELEMENT */, ":").toNode();
|
|
3911
|
-
const np2 = {};
|
|
3912
|
-
const to_delete = [];
|
|
3913
|
-
for (let i = 0; i < arr.length; i++) {
|
|
3914
|
-
if (arr[i].type !== "group") {
|
|
3915
|
-
continue;
|
|
3916
|
-
}
|
|
3917
|
-
const g = arr[i];
|
|
3918
|
-
const pos_colon = array_find(g.items, COLON);
|
|
3919
|
-
if (pos_colon === -1 || pos_colon === 0) {
|
|
3920
|
-
continue;
|
|
3921
|
-
}
|
|
3922
|
-
to_delete.push(i);
|
|
3923
|
-
const param_name = g.items[pos_colon - 1];
|
|
3924
|
-
if (param_name.eq(new TypstToken(1 /* SYMBOL */, "delim").toNode())) {
|
|
3925
|
-
if (g.items.length !== 3) {
|
|
3926
|
-
throw new TypstParserError("Invalid number of arguments for delim");
|
|
3927
|
-
}
|
|
3928
|
-
np2["delim"] = g.items[pos_colon + 1];
|
|
3929
|
-
} else {
|
|
3930
|
-
throw new TypstParserError("Not implemented for other named parameters");
|
|
3931
|
-
}
|
|
3932
|
-
}
|
|
3933
|
-
for (let i = to_delete.length - 1; i >= 0; i--) {
|
|
3934
|
-
arr.splice(to_delete[i], 1);
|
|
3935
|
-
}
|
|
3936
|
-
return [arr, np2];
|
|
3937
|
-
};
|
|
3938
|
-
var extract_named_params = extract_named_params2;
|
|
3939
3942
|
let next_stop = array_find(tokens, rowSepToken, pos);
|
|
3940
|
-
if (next_stop === -1) {
|
|
3943
|
+
if (next_stop === -1 || next_stop > end) {
|
|
3941
3944
|
next_stop = end;
|
|
3942
3945
|
}
|
|
3943
3946
|
let row = this.parseArgumentsWithSeparator(tokens, pos, next_stop, cellSepToken);
|
|
3944
3947
|
let np = {};
|
|
3945
|
-
[row, np] =
|
|
3948
|
+
[row, np] = extract_named_params(row);
|
|
3946
3949
|
matrix.push(row);
|
|
3947
3950
|
Object.assign(named_params, np);
|
|
3948
3951
|
pos = next_stop + 1;
|
|
@@ -3955,26 +3958,15 @@ var TypstParser = class {
|
|
|
3955
3958
|
const args = [];
|
|
3956
3959
|
let pos = start;
|
|
3957
3960
|
while (pos < end) {
|
|
3958
|
-
let nodes = [];
|
|
3959
|
-
while (pos < end) {
|
|
3960
|
-
if (tokens[pos].eq(sepToken)) {
|
|
3961
|
-
pos += 1;
|
|
3962
|
-
break;
|
|
3963
|
-
} else if (tokens[pos].eq(SINGLE_SPACE)) {
|
|
3964
|
-
pos += 1;
|
|
3965
|
-
continue;
|
|
3966
|
-
}
|
|
3967
|
-
const [argItem, newPos] = this.parseNextExpr(tokens, pos);
|
|
3968
|
-
pos = newPos;
|
|
3969
|
-
nodes.push(argItem);
|
|
3970
|
-
}
|
|
3971
3961
|
let arg;
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3962
|
+
let newPos;
|
|
3963
|
+
const env = { spaceSensitive: false, newlineSensitive: true };
|
|
3964
|
+
[arg, newPos] = this.parseUntil(tokens.slice(0, end), pos, sepToken, env);
|
|
3965
|
+
if (newPos == -1) {
|
|
3966
|
+
[arg, newPos] = this.parseUntil(tokens.slice(0, end), pos, null, env);
|
|
3976
3967
|
}
|
|
3977
3968
|
args.push(arg);
|
|
3969
|
+
pos = newPos;
|
|
3978
3970
|
}
|
|
3979
3971
|
return args;
|
|
3980
3972
|
}
|
|
@@ -3993,6 +3985,14 @@ var TexWriter = class {
|
|
|
3993
3985
|
this.queue = this.queue.concat(node.serialize());
|
|
3994
3986
|
}
|
|
3995
3987
|
flushQueue() {
|
|
3988
|
+
while (this.queue.length > 0) {
|
|
3989
|
+
const last_token = this.queue[this.queue.length - 1];
|
|
3990
|
+
if (last_token.eq(TexToken.COMMAND_DISPLAYSTYLE) || last_token.eq(TexToken.COMMAND_TEXTSTYLE)) {
|
|
3991
|
+
this.queue.pop();
|
|
3992
|
+
} else {
|
|
3993
|
+
break;
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
3996
|
for (let i = 0; i < this.queue.length; i++) {
|
|
3997
3997
|
this.buffer = writeTexTokenBuffer(this.buffer, this.queue[i]);
|
|
3998
3998
|
}
|
|
@@ -4005,7 +4005,7 @@ var TexWriter = class {
|
|
|
4005
4005
|
};
|
|
4006
4006
|
|
|
4007
4007
|
// src/index.ts
|
|
4008
|
-
function tex2typst(tex, options) {
|
|
4008
|
+
function tex2typst(tex, options = {}) {
|
|
4009
4009
|
const opt = {
|
|
4010
4010
|
nonStrict: true,
|
|
4011
4011
|
preferShorthands: true,
|
|
@@ -4015,14 +4015,12 @@ function tex2typst(tex, options) {
|
|
|
4015
4015
|
optimize: true,
|
|
4016
4016
|
customTexMacros: {}
|
|
4017
4017
|
};
|
|
4018
|
-
if (options !==
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
opt[key] = options[key];
|
|
4025
|
-
}
|
|
4018
|
+
if (typeof options !== "object") {
|
|
4019
|
+
throw new Error("options must be an object");
|
|
4020
|
+
}
|
|
4021
|
+
for (const key in opt) {
|
|
4022
|
+
if (key in options) {
|
|
4023
|
+
opt[key] = options[key];
|
|
4026
4024
|
}
|
|
4027
4025
|
}
|
|
4028
4026
|
const texTree = parseTex(tex, opt.customTexMacros);
|
|
@@ -4031,9 +4029,20 @@ function tex2typst(tex, options) {
|
|
|
4031
4029
|
writer.serialize(typstTree);
|
|
4032
4030
|
return writer.finalize();
|
|
4033
4031
|
}
|
|
4034
|
-
function typst2tex(typst) {
|
|
4032
|
+
function typst2tex(typst, options = {}) {
|
|
4033
|
+
const opt = {
|
|
4034
|
+
blockMathMode: true
|
|
4035
|
+
};
|
|
4036
|
+
if (typeof options !== "object") {
|
|
4037
|
+
throw new Error("options must be an object");
|
|
4038
|
+
}
|
|
4039
|
+
for (const key in opt) {
|
|
4040
|
+
if (key in options) {
|
|
4041
|
+
opt[key] = options[key];
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
4035
4044
|
const typstTree = parseTypst(typst);
|
|
4036
|
-
const texTree = convert_typst_node_to_tex(typstTree);
|
|
4045
|
+
const texTree = convert_typst_node_to_tex(typstTree, opt);
|
|
4037
4046
|
const writer = new TexWriter();
|
|
4038
4047
|
writer.append(texTree);
|
|
4039
4048
|
return writer.finalize();
|