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.js CHANGED
@@ -1,4 +1,4 @@
1
- // src/util.ts
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
- const allRows = [];
1068
- let row = [];
1069
- allRows.push(row);
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
- if (allRows.length > 0 && allRows[allRows.length - 1].length > 0) {
1102
- const last_cell = allRows[allRows.length - 1][allRows[allRows.length - 1].length - 1];
1103
- if (last_cell.type === "ordgroup") {
1104
- const last_cell_items = last_cell.items;
1105
- while (last_cell_items.length > 0 && [5 /* SPACE */, 6 /* NEWLINE */].includes(last_cell_items[last_cell_items.length - 1].head.type)) {
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 + 1];
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
- if (reverseSymbolMap.has(symbol)) {
3183
- return "\\" + reverseSymbolMap.get(symbol);
3184
- } else {
3185
- return "\\" + symbol;
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(convert_typst_node_to_tex);
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 = convert_typst_node_to_tex(node.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 = convert_typst_node_to_tex(arg0);
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 = convert_typst_node_to_tex(arg0);
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 = convert_typst_node_to_tex(degree);
3332
- return new TexFuncCall(new TexToken(2 /* COMMAND */, "\\sqrt"), [convert_typst_node_to_tex(radicand)], data);
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), [convert_typst_node_to_tex(body)]);
3339
- const script = convert_typst_node_to_tex(label);
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(convert_typst_node_to_tex).map((n) => [n]);
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
- [convert_typst_node_to_tex(node.args[1])]
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(convert_typst_node_to_tex));
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(convert_typst_node_to_tex), TEX_NODE_COMMA),
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
- [convert_typst_node_to_tex(color), convert_typst_node_to_tex(node.fragments[0])]
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 ? convert_typst_node_to_tex(sup) : null;
3418
- const sub_tex = sub ? convert_typst_node_to_tex(sub) : null;
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 = convert_typst_node_to_tex(limits.args[0]);
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 = convert_typst_node_to_tex(base);
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(convert_typst_node_to_tex));
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 = convert_typst_node_to_tex(numerator);
3489
- const den_tex = convert_typst_node_to_tex(denominator);
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 find_closing_delim(tokens, start) {
3606
- return _find_closing_match(
3607
- tokens,
3608
- start,
3609
- TypstToken.LEFT_DELIMITERS,
3610
- TypstToken.RIGHT_DELIMITERS
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
- if (nodes[pos].eq(left_parenthesis)) {
3624
- count += 1;
3625
- } else if (nodes[pos].eq(right_parenthesis)) {
3626
- count -= 1;
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
- return pos - 1;
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, parenthesis = false) {
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 current = nodes[pos];
3678
- if (current.eq(closing_bracket)) {
3679
- throw new TypstParserError("Unexpected ')'");
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
- const body = args.length === 1 ? args[0] : new TypstGroup(args);
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, parentheses = false) {
3759
- const results = [];
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
- const pos_closing = find_closing_match(tokens, start);
3818
- [node, end] = this.parseGroup(tokens, start + 1, pos_closing);
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 pos_closing = find_closing_match(tokens, start);
3834
- return this.parseGroup(tokens, start + 1, pos_closing, true);
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 = tokens[start];
3914
+ const lr_token = new TypstToken(1 /* SYMBOL */, "lr");
3883
3915
  const end = find_closing_match(tokens, start);
3884
- if (tokens[start + 1].isOneOf(TypstToken.LEFT_DELIMITERS)) {
3885
- const inner_start = start + 1;
3886
- const inner_end = find_closing_delim(tokens, inner_start);
3887
- const [inner_args, _] = this.parseGroup(tokens, inner_start + 1, inner_end);
3888
- return [
3889
- new TypstLeftright(lr_token, { body: inner_args, left: tokens[inner_start], right: tokens[inner_end] }),
3890
- end + 1
3891
- ];
3892
- } else {
3893
- const [inner_args, _] = this.parseGroup(tokens, start + 1, end - 1);
3894
- return [
3895
- new TypstLeftright(lr_token, { body: inner_args, left: null, right: null }),
3896
- end + 1
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] = extract_named_params2(row);
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
- if (nodes.length === 1) {
3973
- arg = nodes[0];
3974
- } else {
3975
- arg = process_operators(nodes);
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 !== void 0) {
4019
- if (typeof options !== "object") {
4020
- throw new Error("options must be an object");
4021
- }
4022
- for (const key in opt) {
4023
- if (key in options) {
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();