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 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
- let tokens = this.args.map((n) => n.serialize()).flat();
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
- 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
- ];
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
- const arg1 = match[2].trimStart();
1792
- return [
1793
- new TexToken(1 /* COMMAND */, command),
1794
- new TexToken(0 /* ELEMENT */, arg1)
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 body = node.args[0];
2905
- if (body.type === "group") {
2906
- let left_delim = body.args[0].content;
2907
- let right_delim = body.args[body.args.length - 1].content;
2908
- left_delim = apply_escape_if_needed2(left_delim);
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
- ...body.args.slice(1, body.args.length - 1).map(convert_typst_node_to_tex),
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 find_closing_match2(tokens, start) {
3108
- assert(tokens[start].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2]));
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([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2])) {
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 arg = new TypstNode("group", "", []);
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
- arg.args.push(argItem);
3531
+ nodes.push(argItem);
3456
3532
  }
3457
- if (arg.args.length === 0) {
3533
+ let arg;
3534
+ if (nodes.length === 0) {
3458
3535
  arg = TYPST_EMPTY_NODE;
3459
- } else if (arg.args.length === 1) {
3460
- arg = arg.args[0];
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
  }