tex2typst 0.3.7 → 0.3.9
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/generic.d.ts +1 -0
- package/dist/index.js +148 -69
- package/dist/tex2typst.min.js +13 -13
- package/dist/types.d.ts +7 -3
- package/dist/typst-parser.d.ts +2 -1
- package/package.json +1 -1
- package/src/convert.ts +39 -9
- package/src/generic.ts +13 -0
- package/src/tex-parser.ts +23 -13
- package/src/types.ts +32 -39
- package/src/typst-parser.ts +65 -15
package/dist/generic.d.ts
CHANGED
|
@@ -4,4 +4,5 @@ interface IEquatable {
|
|
|
4
4
|
export declare function array_find<T extends IEquatable>(array: T[], item: T, start?: number): number;
|
|
5
5
|
export declare function array_includes<T extends IEquatable>(array: T[], item: T): boolean;
|
|
6
6
|
export declare function array_split<T extends IEquatable>(array: T[], sep: T): T[][];
|
|
7
|
+
export declare function array_join<T>(array: T[], sep: T): T[];
|
|
7
8
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1100,6 +1100,16 @@ function array_split(array, sep) {
|
|
|
1100
1100
|
res.push(current_slice);
|
|
1101
1101
|
return res;
|
|
1102
1102
|
}
|
|
1103
|
+
function array_join(array, sep) {
|
|
1104
|
+
const res = [];
|
|
1105
|
+
for (let i = 0; i < array.length; i++) {
|
|
1106
|
+
res.push(array[i]);
|
|
1107
|
+
if (i != array.length - 1) {
|
|
1108
|
+
res.push(sep);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
return res;
|
|
1112
|
+
}
|
|
1103
1113
|
|
|
1104
1114
|
// src/types.ts
|
|
1105
1115
|
var TexToken = class {
|
|
@@ -1170,15 +1180,7 @@ var TexNode = class {
|
|
|
1170
1180
|
return tokens;
|
|
1171
1181
|
}
|
|
1172
1182
|
case "ordgroup": {
|
|
1173
|
-
|
|
1174
|
-
if (this.content === "parenthesis") {
|
|
1175
|
-
const is_over_high = this.isOverHigh();
|
|
1176
|
-
const left_delim = is_over_high ? "\\left(" : "(";
|
|
1177
|
-
const right_delim = is_over_high ? "\\right)" : ")";
|
|
1178
|
-
tokens.unshift(new TexToken(0 /* ELEMENT */, left_delim));
|
|
1179
|
-
tokens.push(new TexToken(0 /* ELEMENT */, right_delim));
|
|
1180
|
-
}
|
|
1181
|
-
return tokens;
|
|
1183
|
+
return this.args.map((n) => n.serialize()).flat();
|
|
1182
1184
|
}
|
|
1183
1185
|
case "unaryFunc": {
|
|
1184
1186
|
let tokens = [];
|
|
@@ -1276,33 +1278,6 @@ var TexNode = class {
|
|
|
1276
1278
|
throw new Error("[TexNode.serialize] Unimplemented type: " + this.type);
|
|
1277
1279
|
}
|
|
1278
1280
|
}
|
|
1279
|
-
// whether the node is over high so that if it's wrapped in braces, \left and \right should be used.
|
|
1280
|
-
// e.g. \frac{1}{2} is over high, "2" is not.
|
|
1281
|
-
isOverHigh() {
|
|
1282
|
-
switch (this.type) {
|
|
1283
|
-
case "element":
|
|
1284
|
-
case "symbol":
|
|
1285
|
-
case "text":
|
|
1286
|
-
case "control":
|
|
1287
|
-
case "empty":
|
|
1288
|
-
return false;
|
|
1289
|
-
case "binaryFunc":
|
|
1290
|
-
if (this.content === "\\frac") {
|
|
1291
|
-
return true;
|
|
1292
|
-
}
|
|
1293
|
-
// fall through
|
|
1294
|
-
case "unaryFunc":
|
|
1295
|
-
case "ordgroup":
|
|
1296
|
-
return this.args.some((n) => n.isOverHigh());
|
|
1297
|
-
case "supsub": {
|
|
1298
|
-
return this.data.base.isOverHigh();
|
|
1299
|
-
}
|
|
1300
|
-
case "beginend":
|
|
1301
|
-
return true;
|
|
1302
|
-
default:
|
|
1303
|
-
return false;
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
1281
|
};
|
|
1307
1282
|
var TypstToken = class {
|
|
1308
1283
|
constructor(type, content) {
|
|
@@ -1374,6 +1349,30 @@ var TypstNode = class {
|
|
|
1374
1349
|
eq(other) {
|
|
1375
1350
|
return this.type === other.type && this.content === other.content;
|
|
1376
1351
|
}
|
|
1352
|
+
// whether the node is over high so that if it's wrapped in braces, \left and \right should be used in its TeX form
|
|
1353
|
+
// e.g. 1/2 is over high, "2" is not.
|
|
1354
|
+
isOverHigh() {
|
|
1355
|
+
switch (this.type) {
|
|
1356
|
+
case "fraction":
|
|
1357
|
+
return true;
|
|
1358
|
+
case "funcCall": {
|
|
1359
|
+
if (this.content === "frac") {
|
|
1360
|
+
return true;
|
|
1361
|
+
}
|
|
1362
|
+
return this.args.some((n) => n.isOverHigh());
|
|
1363
|
+
}
|
|
1364
|
+
case "group":
|
|
1365
|
+
return this.args.some((n) => n.isOverHigh());
|
|
1366
|
+
case "supsub":
|
|
1367
|
+
return this.data.base.isOverHigh();
|
|
1368
|
+
case "align":
|
|
1369
|
+
case "cases":
|
|
1370
|
+
case "matrix":
|
|
1371
|
+
return true;
|
|
1372
|
+
default:
|
|
1373
|
+
return false;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1377
1376
|
};
|
|
1378
1377
|
|
|
1379
1378
|
// src/util.ts
|
|
@@ -1774,13 +1773,18 @@ var rules_map = /* @__PURE__ */ new Map([
|
|
|
1774
1773
|
const match = text.match(regex);
|
|
1775
1774
|
assert(match !== null);
|
|
1776
1775
|
const command = match[1];
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1776
|
+
if (BINARY_COMMANDS.includes(command.substring(1))) {
|
|
1777
|
+
const arg1 = match[2].trimStart();
|
|
1778
|
+
const arg2 = match[3];
|
|
1779
|
+
return [
|
|
1780
|
+
new TexToken(1 /* COMMAND */, command),
|
|
1781
|
+
new TexToken(0 /* ELEMENT */, arg1),
|
|
1782
|
+
new TexToken(0 /* ELEMENT */, arg2)
|
|
1783
|
+
];
|
|
1784
|
+
} else {
|
|
1785
|
+
s.reject();
|
|
1786
|
+
return [];
|
|
1787
|
+
}
|
|
1784
1788
|
}],
|
|
1785
1789
|
[String.raw`(\\[a-zA-Z]+)(\s*\d|\s+[a-zA-Z])`, (s) => {
|
|
1786
1790
|
const text = s.text();
|
|
@@ -1788,11 +1792,16 @@ var rules_map = /* @__PURE__ */ new Map([
|
|
|
1788
1792
|
const match = text.match(regex);
|
|
1789
1793
|
assert(match !== null);
|
|
1790
1794
|
const command = match[1];
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1795
|
+
if (UNARY_COMMANDS.includes(command.substring(1))) {
|
|
1796
|
+
const arg1 = match[2].trimStart();
|
|
1797
|
+
return [
|
|
1798
|
+
new TexToken(1 /* COMMAND */, command),
|
|
1799
|
+
new TexToken(0 /* ELEMENT */, arg1)
|
|
1800
|
+
];
|
|
1801
|
+
} else {
|
|
1802
|
+
s.reject();
|
|
1803
|
+
return [];
|
|
1804
|
+
}
|
|
1796
1805
|
}],
|
|
1797
1806
|
[String.raw`\\[a-zA-Z]+`, (s) => {
|
|
1798
1807
|
const command = s.text();
|
|
@@ -2840,7 +2849,9 @@ var TYPST_UNARY_FUNCTIONS = [
|
|
|
2840
2849
|
"underline",
|
|
2841
2850
|
"bb",
|
|
2842
2851
|
"cal",
|
|
2843
|
-
"frak"
|
|
2852
|
+
"frak",
|
|
2853
|
+
"floor",
|
|
2854
|
+
"ceil"
|
|
2844
2855
|
];
|
|
2845
2856
|
var TYPST_BINARY_FUNCTIONS = [
|
|
2846
2857
|
"frac",
|
|
@@ -2864,6 +2875,7 @@ function typst_token_to_tex(token) {
|
|
|
2864
2875
|
}
|
|
2865
2876
|
return "\\" + token;
|
|
2866
2877
|
}
|
|
2878
|
+
var TEX_NODE_COMMA = new TexNode("element", ",");
|
|
2867
2879
|
function convert_typst_node_to_tex(node) {
|
|
2868
2880
|
if (node.eq(new TypstNode("symbol", "eq.def"))) {
|
|
2869
2881
|
return new TexNode("binaryFunc", "\\overset", [
|
|
@@ -2896,23 +2908,45 @@ function convert_typst_node_to_tex(node) {
|
|
|
2896
2908
|
return new TexNode("comment", node.content);
|
|
2897
2909
|
case "group": {
|
|
2898
2910
|
const args = node.args.map(convert_typst_node_to_tex);
|
|
2911
|
+
if (node.content === "parenthesis") {
|
|
2912
|
+
const is_over_high = node.isOverHigh();
|
|
2913
|
+
const left_delim = is_over_high ? "\\left(" : "(";
|
|
2914
|
+
const right_delim = is_over_high ? "\\right)" : ")";
|
|
2915
|
+
args.unshift(new TexNode("element", left_delim));
|
|
2916
|
+
args.push(new TexNode("element", right_delim));
|
|
2917
|
+
}
|
|
2899
2918
|
return new TexNode("ordgroup", node.content, args);
|
|
2900
2919
|
}
|
|
2901
2920
|
case "funcCall": {
|
|
2902
2921
|
if (TYPST_UNARY_FUNCTIONS.includes(node.content)) {
|
|
2903
2922
|
if (node.content === "lr") {
|
|
2904
|
-
const
|
|
2905
|
-
if (
|
|
2906
|
-
let left_delim =
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
right_delim = apply_escape_if_needed2(right_delim);
|
|
2923
|
+
const data = node.data;
|
|
2924
|
+
if (data.leftDelim !== null) {
|
|
2925
|
+
let left_delim = apply_escape_if_needed2(data.leftDelim);
|
|
2926
|
+
assert(data.rightDelim !== null, "leftDelim has value but rightDelim not");
|
|
2927
|
+
let right_delim = apply_escape_if_needed2(data.rightDelim);
|
|
2910
2928
|
return new TexNode("ordgroup", "", [
|
|
2911
2929
|
new TexNode("element", "\\left" + left_delim),
|
|
2912
|
-
...
|
|
2930
|
+
...node.args.map(convert_typst_node_to_tex),
|
|
2913
2931
|
new TexNode("element", "\\right" + right_delim)
|
|
2914
2932
|
]);
|
|
2933
|
+
} else {
|
|
2934
|
+
return new TexNode("ordgroup", "", node.args.map(convert_typst_node_to_tex));
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
if (node.content === "floor" || node.content === "ceil") {
|
|
2938
|
+
let left = "\\l" + node.content;
|
|
2939
|
+
let right = "\\r" + node.content;
|
|
2940
|
+
const arg0 = node.args[0];
|
|
2941
|
+
if (arg0.isOverHigh()) {
|
|
2942
|
+
left = "\\left" + left;
|
|
2943
|
+
right = "\\right" + right;
|
|
2915
2944
|
}
|
|
2945
|
+
return new TexNode("ordgroup", "", [
|
|
2946
|
+
new TexNode("symbol", left),
|
|
2947
|
+
convert_typst_node_to_tex(arg0),
|
|
2948
|
+
new TexNode("symbol", right)
|
|
2949
|
+
]);
|
|
2916
2950
|
}
|
|
2917
2951
|
const command = typst_token_to_tex(node.content);
|
|
2918
2952
|
return new TexNode("unaryFunc", command, node.args.map(convert_typst_node_to_tex));
|
|
@@ -2935,7 +2969,7 @@ function convert_typst_node_to_tex(node) {
|
|
|
2935
2969
|
return new TexNode("ordgroup", "", [
|
|
2936
2970
|
new TexNode("symbol", typst_token_to_tex(node.content)),
|
|
2937
2971
|
new TexNode("element", "("),
|
|
2938
|
-
...node.args.map(convert_typst_node_to_tex),
|
|
2972
|
+
...array_join(node.args.map(convert_typst_node_to_tex), TEX_NODE_COMMA),
|
|
2939
2973
|
new TexNode("element", ")")
|
|
2940
2974
|
]);
|
|
2941
2975
|
}
|
|
@@ -3104,23 +3138,39 @@ function tokenize_typst(input) {
|
|
|
3104
3138
|
const lexer = new JSLex(spec2);
|
|
3105
3139
|
return lexer.collect(input);
|
|
3106
3140
|
}
|
|
3107
|
-
function
|
|
3108
|
-
assert(tokens[start].isOneOf(
|
|
3141
|
+
function _find_closing_match(tokens, start, leftBrackets, rightBrackets) {
|
|
3142
|
+
assert(tokens[start].isOneOf(leftBrackets));
|
|
3109
3143
|
let count = 1;
|
|
3110
3144
|
let pos = start + 1;
|
|
3111
3145
|
while (count > 0) {
|
|
3112
3146
|
if (pos >= tokens.length) {
|
|
3113
3147
|
throw new Error("Unmatched brackets");
|
|
3114
3148
|
}
|
|
3115
|
-
if (tokens[pos].isOneOf(
|
|
3116
|
-
count += 1;
|
|
3117
|
-
} else if (tokens[pos].isOneOf([RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2])) {
|
|
3149
|
+
if (tokens[pos].isOneOf(rightBrackets)) {
|
|
3118
3150
|
count -= 1;
|
|
3151
|
+
} else if (tokens[pos].isOneOf(leftBrackets)) {
|
|
3152
|
+
count += 1;
|
|
3119
3153
|
}
|
|
3120
3154
|
pos += 1;
|
|
3121
3155
|
}
|
|
3122
3156
|
return pos - 1;
|
|
3123
3157
|
}
|
|
3158
|
+
function find_closing_match2(tokens, start) {
|
|
3159
|
+
return _find_closing_match(
|
|
3160
|
+
tokens,
|
|
3161
|
+
start,
|
|
3162
|
+
[LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2],
|
|
3163
|
+
[RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2]
|
|
3164
|
+
);
|
|
3165
|
+
}
|
|
3166
|
+
function find_closing_delim(tokens, start) {
|
|
3167
|
+
return _find_closing_match(
|
|
3168
|
+
tokens,
|
|
3169
|
+
start,
|
|
3170
|
+
[LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2, VERTICAL_BAR],
|
|
3171
|
+
[RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2, VERTICAL_BAR]
|
|
3172
|
+
);
|
|
3173
|
+
}
|
|
3124
3174
|
function find_closing_parenthesis(nodes, start) {
|
|
3125
3175
|
const left_parenthesis = new TypstNode("atom", "(");
|
|
3126
3176
|
const right_parenthesis = new TypstNode("atom", ")");
|
|
@@ -3246,6 +3296,7 @@ var LEFT_BRACKET = new TypstToken(1 /* ELEMENT */, "[");
|
|
|
3246
3296
|
var RIGHT_BRACKET = new TypstToken(1 /* ELEMENT */, "]");
|
|
3247
3297
|
var LEFT_CURLY_BRACKET2 = new TypstToken(1 /* ELEMENT */, "{");
|
|
3248
3298
|
var RIGHT_CURLY_BRACKET2 = new TypstToken(1 /* ELEMENT */, "}");
|
|
3299
|
+
var VERTICAL_BAR = new TypstToken(1 /* ELEMENT */, "|");
|
|
3249
3300
|
var COMMA = new TypstToken(1 /* ELEMENT */, ",");
|
|
3250
3301
|
var SEMICOLON = new TypstToken(1 /* ELEMENT */, ";");
|
|
3251
3302
|
var SINGLE_SPACE = new TypstToken(4 /* SPACE */, " ");
|
|
@@ -3361,9 +3412,13 @@ var TypstParser = class {
|
|
|
3361
3412
|
casesNode.setOptions(named_params);
|
|
3362
3413
|
return [casesNode, newPos2];
|
|
3363
3414
|
}
|
|
3415
|
+
if (firstToken.value === "lr") {
|
|
3416
|
+
const [args2, newPos2, lrData] = this.parseLrArguments(tokens, start + 1);
|
|
3417
|
+
const func_call2 = new TypstNode("funcCall", firstToken.value, args2, lrData);
|
|
3418
|
+
return [func_call2, newPos2];
|
|
3419
|
+
}
|
|
3364
3420
|
const [args, newPos] = this.parseArguments(tokens, start + 1);
|
|
3365
|
-
const func_call = new TypstNode("funcCall", firstToken.value);
|
|
3366
|
-
func_call.args = args;
|
|
3421
|
+
const func_call = new TypstNode("funcCall", firstToken.value, args);
|
|
3367
3422
|
return [func_call, newPos];
|
|
3368
3423
|
}
|
|
3369
3424
|
}
|
|
@@ -3375,6 +3430,27 @@ var TypstParser = class {
|
|
|
3375
3430
|
return [this.parseCommaSeparatedArguments(tokens, start + 1, end), end + 1];
|
|
3376
3431
|
}
|
|
3377
3432
|
// start: the position of the left parentheses
|
|
3433
|
+
parseLrArguments(tokens, start) {
|
|
3434
|
+
if (tokens[start + 1].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2, VERTICAL_BAR])) {
|
|
3435
|
+
const end = find_closing_match2(tokens, start);
|
|
3436
|
+
const inner_start = start + 1;
|
|
3437
|
+
const inner_end = find_closing_delim(tokens, inner_start);
|
|
3438
|
+
const inner_args = this.parseCommaSeparatedArguments(tokens, inner_start + 1, inner_end);
|
|
3439
|
+
return [
|
|
3440
|
+
inner_args,
|
|
3441
|
+
end + 1,
|
|
3442
|
+
{ leftDelim: tokens[inner_start].value, rightDelim: tokens[inner_end].value }
|
|
3443
|
+
];
|
|
3444
|
+
} else {
|
|
3445
|
+
const [args, end] = this.parseArguments(tokens, start);
|
|
3446
|
+
return [
|
|
3447
|
+
args,
|
|
3448
|
+
end,
|
|
3449
|
+
{ leftDelim: null, rightDelim: null }
|
|
3450
|
+
];
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
// start: the position of the left parentheses
|
|
3378
3454
|
parseGroupsOfArguments(tokens, start, newline_token = SEMICOLON) {
|
|
3379
3455
|
const end = find_closing_match2(tokens, start);
|
|
3380
3456
|
tokens = tokens.slice(0, end);
|
|
@@ -3441,7 +3517,7 @@ var TypstParser = class {
|
|
|
3441
3517
|
const args = [];
|
|
3442
3518
|
let pos = start;
|
|
3443
3519
|
while (pos < end) {
|
|
3444
|
-
let
|
|
3520
|
+
let nodes = [];
|
|
3445
3521
|
while (pos < end) {
|
|
3446
3522
|
if (tokens[pos].eq(COMMA)) {
|
|
3447
3523
|
pos += 1;
|
|
@@ -3452,12 +3528,15 @@ var TypstParser = class {
|
|
|
3452
3528
|
}
|
|
3453
3529
|
const [argItem, newPos] = this.parseNextExpr(tokens, pos);
|
|
3454
3530
|
pos = newPos;
|
|
3455
|
-
|
|
3531
|
+
nodes.push(argItem);
|
|
3456
3532
|
}
|
|
3457
|
-
|
|
3533
|
+
let arg;
|
|
3534
|
+
if (nodes.length === 0) {
|
|
3458
3535
|
arg = TYPST_EMPTY_NODE;
|
|
3459
|
-
} else if (
|
|
3460
|
-
arg =
|
|
3536
|
+
} else if (nodes.length === 1) {
|
|
3537
|
+
arg = nodes[0];
|
|
3538
|
+
} else {
|
|
3539
|
+
arg = process_operators(nodes);
|
|
3461
3540
|
}
|
|
3462
3541
|
args.push(arg);
|
|
3463
3542
|
}
|