tex2typst 0.3.27-beta.1 → 0.3.28

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
@@ -278,6 +278,9 @@ function array_split(array, sep) {
278
278
  res.push(current_slice);
279
279
  return res;
280
280
  }
281
+ function array_join(arrays, sep) {
282
+ return arrays.flatMap((arr, i) => i !== arrays.length - 1 ? [...arr, sep] : arr);
283
+ }
281
284
  function array_intersperse(array, sep) {
282
285
  return array.flatMap((x, i) => i !== array.length - 1 ? [x, sep] : [x]);
283
286
  }
@@ -563,7 +566,13 @@ var TEX_UNARY_COMMANDS = [
563
566
  "overleftarrow",
564
567
  "overrightarrow",
565
568
  "hspace",
566
- "substack"
569
+ "substack",
570
+ "set",
571
+ "displaylines",
572
+ "mathinner",
573
+ "mathrel",
574
+ "mathbin",
575
+ "mathop"
567
576
  ];
568
577
  var TEX_BINARY_COMMANDS = [
569
578
  "frac",
@@ -707,7 +716,7 @@ function eat_parenthesis(tokens, start) {
707
716
  const firstToken = tokens[start];
708
717
  if (firstToken.type === 1 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", ".", "\\|"].includes(firstToken.value)) {
709
718
  return firstToken;
710
- } else if (firstToken.type === 2 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle"].includes(firstToken.value.slice(1))) {
719
+ } else if (firstToken.type === 2 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle", "lparen", "rparen", "lbrace", "rbrace"].includes(firstToken.value.slice(1))) {
711
720
  return firstToken;
712
721
  } else {
713
722
  return null;
@@ -720,44 +729,32 @@ function eat_primes(tokens, start) {
720
729
  }
721
730
  return pos - start;
722
731
  }
723
- function find_closing_match(tokens, start, leftToken, rightToken) {
724
- assert(tokens[start].eq(leftToken));
725
- let count = 1;
726
- let pos = start + 1;
727
- while (count > 0) {
728
- if (pos >= tokens.length) {
729
- return -1;
730
- }
731
- if (tokens[pos].eq(leftToken)) {
732
- count += 1;
733
- } else if (tokens[pos].eq(rightToken)) {
734
- count -= 1;
735
- }
736
- pos += 1;
737
- }
738
- return pos - 1;
739
- }
740
732
  var LEFT_COMMAND = new TexToken(2 /* COMMAND */, "\\left");
741
733
  var RIGHT_COMMAND = new TexToken(2 /* COMMAND */, "\\right");
742
- function find_closing_right_command(tokens, start) {
743
- return find_closing_match(tokens, start, LEFT_COMMAND, RIGHT_COMMAND);
744
- }
745
734
  var BEGIN_COMMAND = new TexToken(2 /* COMMAND */, "\\begin");
746
735
  var END_COMMAND = new TexToken(2 /* COMMAND */, "\\end");
747
- function find_closing_end_command(tokens, start) {
748
- return find_closing_match(tokens, start, BEGIN_COMMAND, END_COMMAND);
749
- }
750
- var LatexParserError = class extends Error {
736
+ var CONTROL_LINEBREAK = new TexToken(7 /* CONTROL */, "\\\\");
737
+ var LatexParserError = class _LatexParserError extends Error {
751
738
  constructor(message) {
752
739
  super(message);
753
740
  this.name = "LatexParserError";
754
741
  }
742
+ static UNMATCHED_LEFT_BRACE = new _LatexParserError("Unmatched '\\{'");
743
+ static UNMATCHED_RIGHT_BRACE = new _LatexParserError("Unmatched '\\}'");
744
+ static UNMATCHED_LEFT_BRACKET = new _LatexParserError("Unmatched '\\['");
745
+ static UNMATCHED_RIGHT_BRACKET = new _LatexParserError("Unmatched '\\]'");
746
+ static UNMATCHED_COMMAND_BEGIN = new _LatexParserError("Unmatched '\\begin'");
747
+ static UNMATCHED_COMMAND_END = new _LatexParserError("Unmatched '\\end'");
748
+ static UNMATCHED_COMMAND_LEFT = new _LatexParserError("Unmatched '\\left'");
749
+ static UNMATCHED_COMMAND_RIGHT = new _LatexParserError("Unmatched '\\right'");
755
750
  };
756
751
  var SUB_SYMBOL = new TexToken(7 /* CONTROL */, "_");
757
752
  var SUP_SYMBOL = new TexToken(7 /* CONTROL */, "^");
758
753
  var LatexParser = class {
759
754
  space_sensitive;
760
755
  newline_sensitive;
756
+ // how many levels of \begin{...} \end{...} are we currently in
757
+ alignmentDepth = 0;
761
758
  constructor(space_sensitive = false, newline_sensitive = true) {
762
759
  this.space_sensitive = space_sensitive;
763
760
  this.newline_sensitive = newline_sensitive;
@@ -766,22 +763,30 @@ var LatexParser = class {
766
763
  const token_displaystyle = new TexToken(2 /* COMMAND */, "\\displaystyle");
767
764
  const idx = array_find(tokens, token_displaystyle);
768
765
  if (idx === -1) {
769
- const [tree, _] = this.parseGroup(tokens, 0, tokens.length);
770
- return tree;
766
+ return this.parseGroup(tokens.slice(0));
771
767
  } else if (idx === 0) {
772
- const [tree, _] = this.parseGroup(tokens, 1, tokens.length);
768
+ const tree = this.parseGroup(tokens.slice(1));
773
769
  return new TexFuncCall(token_displaystyle, [tree]);
774
770
  } else {
775
- const [tree1, _1] = this.parseGroup(tokens, 0, idx);
776
- const [tree2, _2] = this.parseGroup(tokens, idx + 1, tokens.length);
771
+ const tree1 = this.parseGroup(tokens.slice(0, idx));
772
+ const tree2 = this.parseGroup(tokens.slice(idx + 1, tokens.length));
777
773
  const display = new TexFuncCall(token_displaystyle, [tree2]);
778
774
  return new TexGroup([tree1, display]);
779
775
  }
780
776
  }
781
- parseGroup(tokens, start, end) {
777
+ parseGroup(tokens) {
778
+ const [tree, _] = this.parseClosure(tokens, 0, null);
779
+ return tree;
780
+ }
781
+ // return pos: (position of closingToken) + 1
782
+ // pos will be -1 if closingToken is not found
783
+ parseClosure(tokens, start, closingToken) {
782
784
  const results = [];
783
785
  let pos = start;
784
- while (pos < end) {
786
+ while (pos < tokens.length) {
787
+ if (closingToken !== null && tokens[pos].eq(closingToken)) {
788
+ break;
789
+ }
785
790
  const [res, newPos] = this.parseNextExpr(tokens, pos);
786
791
  pos = newPos;
787
792
  if (res.head.type === 5 /* SPACE */ || res.head.type === 6 /* NEWLINE */) {
@@ -792,18 +797,18 @@ var LatexParser = class {
792
797
  continue;
793
798
  }
794
799
  }
795
- if (res.head.eq(new TexToken(7 /* CONTROL */, "&"))) {
796
- throw new LatexParserError("Unexpected & outside of an alignment");
797
- }
798
800
  results.push(res);
799
801
  }
802
+ if (pos >= tokens.length && closingToken !== null) {
803
+ return [EMPTY_NODE, -1];
804
+ }
800
805
  let node;
801
806
  if (results.length === 1) {
802
807
  node = results[0];
803
808
  } else {
804
809
  node = new TexGroup(results);
805
810
  }
806
- return [node, end + 1];
811
+ return [node, pos + 1];
807
812
  }
808
813
  parseNextExpr(tokens, start) {
809
814
  let [base, pos] = this.parseNextExprWithoutSupSub(tokens, start);
@@ -812,25 +817,28 @@ var LatexParser = class {
812
817
  let num_prime = 0;
813
818
  num_prime += eat_primes(tokens, pos);
814
819
  pos += num_prime;
815
- if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL)) {
816
- [sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
817
- num_prime += eat_primes(tokens, pos);
818
- pos += num_prime;
819
- if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL)) {
820
+ if (pos < tokens.length) {
821
+ const next_token = tokens[pos];
822
+ if (next_token.eq(SUB_SYMBOL)) {
823
+ [sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
824
+ num_prime += eat_primes(tokens, pos);
825
+ pos += num_prime;
826
+ if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL)) {
827
+ [sup, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
828
+ if (eat_primes(tokens, pos) > 0) {
829
+ throw new LatexParserError("Double superscript");
830
+ }
831
+ }
832
+ } else if (next_token.eq(SUP_SYMBOL)) {
820
833
  [sup, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
821
834
  if (eat_primes(tokens, pos) > 0) {
822
835
  throw new LatexParserError("Double superscript");
823
836
  }
824
- }
825
- } else if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL)) {
826
- [sup, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
827
- if (eat_primes(tokens, pos) > 0) {
828
- throw new LatexParserError("Double superscript");
829
- }
830
- if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL)) {
831
- [sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
832
- if (eat_primes(tokens, pos) > 0) {
833
- throw new LatexParserError("Double superscript");
837
+ if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL)) {
838
+ [sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
839
+ if (eat_primes(tokens, pos) > 0) {
840
+ throw new LatexParserError("Double superscript");
841
+ }
834
842
  }
835
843
  }
836
844
  }
@@ -858,7 +866,7 @@ var LatexParser = class {
858
866
  }
859
867
  parseNextExprWithoutSupSub(tokens, start) {
860
868
  if (start >= tokens.length) {
861
- return [EMPTY_NODE, start];
869
+ throw new LatexParserError("Unexpected end of input");
862
870
  }
863
871
  const firstToken = tokens[start];
864
872
  switch (firstToken.type) {
@@ -875,8 +883,12 @@ var LatexParser = class {
875
883
  }
876
884
  if (firstToken.eq(BEGIN_COMMAND)) {
877
885
  return this.parseBeginEndExpr(tokens, start);
886
+ } else if (firstToken.eq(END_COMMAND)) {
887
+ throw LatexParserError.UNMATCHED_COMMAND_END;
878
888
  } else if (firstToken.eq(LEFT_COMMAND)) {
879
889
  return this.parseLeftRightExpr(tokens, start);
890
+ } else if (firstToken.eq(RIGHT_COMMAND)) {
891
+ throw LatexParserError.UNMATCHED_COMMAND_RIGHT;
880
892
  } else {
881
893
  return this.parseCommandExpr(tokens, start);
882
894
  }
@@ -884,13 +896,13 @@ var LatexParser = class {
884
896
  const controlChar = firstToken.value;
885
897
  switch (controlChar) {
886
898
  case "{":
887
- const posClosingBracket = find_closing_match(tokens, start, LEFT_CURLY_BRACKET, RIGHT_CURLY_BRACKET);
888
- if (posClosingBracket === -1) {
889
- throw new LatexParserError("Unmatched '{'");
899
+ const [group, newPos] = this.parseClosure(tokens, start + 1, RIGHT_CURLY_BRACKET);
900
+ if (newPos === -1) {
901
+ throw LatexParserError.UNMATCHED_LEFT_BRACE;
890
902
  }
891
- return this.parseGroup(tokens, start + 1, posClosingBracket);
903
+ return [group, newPos];
892
904
  case "}":
893
- throw new LatexParserError("Unmatched '}'");
905
+ throw LatexParserError.UNMATCHED_RIGHT_BRACE;
894
906
  case "\\\\":
895
907
  case "\\!":
896
908
  case "\\,":
@@ -903,6 +915,9 @@ var LatexParser = class {
903
915
  case "^":
904
916
  return [EMPTY_NODE, start];
905
917
  case "&":
918
+ if (this.alignmentDepth <= 0) {
919
+ throw new LatexParserError("Unexpected & outside of an alignment");
920
+ }
906
921
  return [firstToken.toNode(), start + 1];
907
922
  default:
908
923
  throw new LatexParserError("Unknown control sequence");
@@ -916,9 +931,6 @@ var LatexParser = class {
916
931
  const command_token = tokens[start];
917
932
  const command = command_token.value;
918
933
  let pos = start + 1;
919
- if (["left", "right", "begin", "end"].includes(command.slice(1))) {
920
- throw new LatexParserError("Unexpected command: " + command);
921
- }
922
934
  const paramNum = get_command_param_num(command.slice(1));
923
935
  switch (paramNum) {
924
936
  case 0:
@@ -928,13 +940,11 @@ var LatexParser = class {
928
940
  throw new LatexParserError("Expecting argument for " + command);
929
941
  }
930
942
  if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
931
- const posLeftSquareBracket = pos;
932
- const posRightSquareBracket = find_closing_match(tokens, pos, LEFT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET);
933
- if (posRightSquareBracket === -1) {
934
- throw new LatexParserError("No matching right square bracket for [");
943
+ const [exponent, newPos1] = this.parseClosure(tokens, pos + 1, RIGHT_SQUARE_BRACKET);
944
+ if (newPos1 === -1) {
945
+ throw LatexParserError.UNMATCHED_LEFT_BRACKET;
935
946
  }
936
- const [exponent, _] = this.parseGroup(tokens, posLeftSquareBracket + 1, posRightSquareBracket);
937
- const [arg12, newPos2] = this.parseNextArg(tokens, posRightSquareBracket + 1);
947
+ const [arg12, newPos2] = this.parseNextArg(tokens, newPos1);
938
948
  return [new TexFuncCall(command_token, [arg12], exponent), newPos2];
939
949
  } else if (command === "\\text") {
940
950
  if (pos + 2 >= tokens.length) {
@@ -945,6 +955,14 @@ var LatexParser = class {
945
955
  assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
946
956
  const literal = tokens[pos + 1];
947
957
  return [new TexText(literal), pos + 3];
958
+ } else if (command === "\\displaylines") {
959
+ assert(tokens[pos].eq(LEFT_CURLY_BRACKET));
960
+ const [matrix, newPos2] = this.parseAligned(tokens, pos + 1, RIGHT_CURLY_BRACKET);
961
+ if (newPos2 === -1) {
962
+ throw LatexParserError.UNMATCHED_LEFT_BRACE;
963
+ }
964
+ const group = new TexGroup(array_join(matrix, CONTROL_LINEBREAK.toNode()));
965
+ return [new TexFuncCall(command_token, [group]), newPos2];
948
966
  }
949
967
  let [arg1, newPos] = this.parseNextArg(tokens, pos);
950
968
  return [new TexFuncCall(command_token, [arg1]), newPos];
@@ -986,30 +1004,27 @@ var LatexParser = class {
986
1004
  let pos = start + 1;
987
1005
  pos += eat_whitespaces(tokens, pos).length;
988
1006
  if (pos >= tokens.length) {
989
- throw new LatexParserError("Expecting delimiter after \\left");
1007
+ throw new LatexParserError("Expecting a delimiter after \\left");
990
1008
  }
991
1009
  const leftDelimiter = eat_parenthesis(tokens, pos);
992
1010
  if (leftDelimiter === null) {
993
1011
  throw new LatexParserError("Invalid delimiter after \\left");
994
1012
  }
995
1013
  pos++;
996
- const exprInsideStart = pos;
997
- const idx = find_closing_right_command(tokens, start);
1014
+ const [body, idx] = this.parseClosure(tokens, pos, RIGHT_COMMAND);
998
1015
  if (idx === -1) {
999
- throw new LatexParserError("No matching \\right");
1016
+ throw LatexParserError.UNMATCHED_COMMAND_LEFT;
1000
1017
  }
1001
- const exprInsideEnd = idx;
1002
- pos = idx + 1;
1018
+ pos = idx;
1003
1019
  pos += eat_whitespaces(tokens, pos).length;
1004
1020
  if (pos >= tokens.length) {
1005
- throw new LatexParserError("Expecting \\right after \\left");
1021
+ throw new LatexParserError("Expecting a delimiter after \\right");
1006
1022
  }
1007
1023
  const rightDelimiter = eat_parenthesis(tokens, pos);
1008
1024
  if (rightDelimiter === null) {
1009
1025
  throw new LatexParserError("Invalid delimiter after \\right");
1010
1026
  }
1011
1027
  pos++;
1012
- const [body, _] = this.parseGroup(tokens, exprInsideStart, exprInsideEnd);
1013
1028
  const left = leftDelimiter.value === "." ? null : leftDelimiter;
1014
1029
  const right = rightDelimiter.value === "." ? null : rightDelimiter;
1015
1030
  const res = new TexLeftRight({ body, left, right });
@@ -1028,37 +1043,36 @@ var LatexParser = class {
1028
1043
  pos += eat_whitespaces(tokens, pos).length;
1029
1044
  [data, pos] = this.parseNextArg(tokens, pos);
1030
1045
  }
1031
- pos += eat_whitespaces(tokens, pos).length;
1032
- const exprInsideStart = pos;
1033
- const endIdx = find_closing_end_command(tokens, start);
1046
+ const [body, endIdx] = this.parseAligned(tokens, pos, END_COMMAND);
1034
1047
  if (endIdx === -1) {
1035
- throw new LatexParserError("No matching \\end");
1048
+ throw LatexParserError.UNMATCHED_COMMAND_BEGIN;
1036
1049
  }
1037
- const exprInsideEnd = endIdx;
1038
- pos = endIdx + 1;
1050
+ pos = endIdx;
1039
1051
  assert(tokens[pos].eq(LEFT_CURLY_BRACKET));
1040
1052
  assert(tokens[pos + 1].type === 3 /* LITERAL */);
1041
1053
  assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
1042
1054
  if (tokens[pos + 1].value !== envName) {
1043
- throw new LatexParserError("Mismatched \\begin and \\end environments");
1055
+ throw new LatexParserError("\\begin and \\end environments mismatch");
1044
1056
  }
1045
1057
  pos += 3;
1046
- const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
1047
- while (exprInside.length > 0 && [5 /* SPACE */, 6 /* NEWLINE */].includes(exprInside[exprInside.length - 1].type)) {
1048
- exprInside.pop();
1049
- }
1050
- const body = this.parseAligned(exprInside);
1051
1058
  const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), body, data);
1052
1059
  return [res, pos];
1053
1060
  }
1054
- parseAligned(tokens) {
1055
- let pos = 0;
1061
+ // return pos: (position of closingToken) + 1
1062
+ // pos will be -1 if closingToken is not found
1063
+ parseAligned(tokens, start, closingToken) {
1064
+ this.alignmentDepth++;
1065
+ let pos = start;
1066
+ pos += eat_whitespaces(tokens, pos).length;
1056
1067
  const allRows = [];
1057
1068
  let row = [];
1058
1069
  allRows.push(row);
1059
1070
  let group = new TexGroup([]);
1060
1071
  row.push(group);
1061
1072
  while (pos < tokens.length) {
1073
+ if (tokens[pos].eq(closingToken)) {
1074
+ break;
1075
+ }
1062
1076
  const [res, newPos] = this.parseNextExpr(tokens, pos);
1063
1077
  pos = newPos;
1064
1078
  if (res.head.type === 5 /* SPACE */ || res.head.type === 6 /* NEWLINE */) {
@@ -1081,7 +1095,20 @@ var LatexParser = class {
1081
1095
  group.items.push(res);
1082
1096
  }
1083
1097
  }
1084
- return allRows;
1098
+ if (pos >= tokens.length) {
1099
+ return [[], -1];
1100
+ }
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
+ }
1108
+ }
1109
+ }
1110
+ this.alignmentDepth--;
1111
+ return [allRows, pos + 1];
1085
1112
  }
1086
1113
  };
1087
1114
  function passIgnoreWhitespaceBeforeScriptMark(tokens) {
@@ -1110,7 +1137,7 @@ function passExpandCustomTexMacros(tokens, customTexMacros) {
1110
1137
  }
1111
1138
  return out_tokens;
1112
1139
  }
1113
- function parseTex(tex, customTexMacros) {
1140
+ function parseTex(tex, customTexMacros = {}) {
1114
1141
  const parser = new LatexParser();
1115
1142
  let tokens = tokenize_tex(tex);
1116
1143
  tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
@@ -1118,6 +1145,54 @@ function parseTex(tex, customTexMacros) {
1118
1145
  return parser.parse(tokens);
1119
1146
  }
1120
1147
 
1148
+ // src/typst-shorthands.ts
1149
+ var shorthandMap = /* @__PURE__ */ new Map([
1150
+ ["arrow.l.r.double.long", "<==>"],
1151
+ ["arrow.l.r.long", "<-->"],
1152
+ ["arrow.r.bar", "|->"],
1153
+ ["arrow.r.double.bar", "|=>"],
1154
+ ["arrow.r.double.long", "==>"],
1155
+ ["arrow.r.long", "-->"],
1156
+ ["arrow.r.long.squiggly", "~~>"],
1157
+ ["arrow.r.tail", ">->"],
1158
+ ["arrow.r.twohead", "->>"],
1159
+ ["arrow.l.double.long", "<=="],
1160
+ ["arrow.l.long", "<--"],
1161
+ ["arrow.l.long.squiggly", "<~~"],
1162
+ ["arrow.l.tail", "<-<"],
1163
+ ["arrow.l.twohead", "<<-"],
1164
+ ["arrow.l.r", "<->"],
1165
+ ["arrow.l.r.double", "<=>"],
1166
+ ["colon.double.eq", "::="],
1167
+ ["dots.h", "..."],
1168
+ ["gt.triple", ">>>"],
1169
+ ["lt.triple", "<<<"],
1170
+ ["arrow.r", "->"],
1171
+ ["arrow.r.double", "=>"],
1172
+ ["arrow.r.squiggly", "~>"],
1173
+ ["arrow.l", "<-"],
1174
+ ["arrow.l.squiggly", "<~"],
1175
+ ["bar.v.double", "||"],
1176
+ ["bracket.l.double", "[|"],
1177
+ ["bracket.r.double", "|]"],
1178
+ ["colon.eq", ":="],
1179
+ ["eq.colon", "=:"],
1180
+ ["eq.not", "!="],
1181
+ ["gt.double", ">>"],
1182
+ ["gt.eq", ">="],
1183
+ ["lt.double", "<<"],
1184
+ ["lt.eq", "<="],
1185
+ ["ast.op", "*"],
1186
+ ["minus", "-"],
1187
+ ["tilde.op", "~"]
1188
+ ]);
1189
+ var reverseShorthandMap = /* @__PURE__ */ new Map();
1190
+ for (const [key, value] of shorthandMap.entries()) {
1191
+ if (value.length > 1) {
1192
+ reverseShorthandMap.set(value, key);
1193
+ }
1194
+ }
1195
+
1121
1196
  // src/typst-types.ts
1122
1197
  var TypstToken = class _TypstToken {
1123
1198
  type;
@@ -1147,7 +1222,32 @@ var TypstToken = class _TypstToken {
1147
1222
  }
1148
1223
  static NONE = new _TypstToken(0 /* NONE */, "#none");
1149
1224
  static EMPTY = new _TypstToken(2 /* ELEMENT */, "");
1225
+ static LEFT_BRACE = new _TypstToken(2 /* ELEMENT */, "{");
1226
+ static RIGHT_BRACE = new _TypstToken(2 /* ELEMENT */, "}");
1227
+ static LEFT_DELIMITERS = [
1228
+ new _TypstToken(2 /* ELEMENT */, "("),
1229
+ new _TypstToken(2 /* ELEMENT */, "["),
1230
+ new _TypstToken(2 /* ELEMENT */, "{"),
1231
+ new _TypstToken(2 /* ELEMENT */, "|"),
1232
+ new _TypstToken(1 /* SYMBOL */, "angle.l")
1233
+ ];
1234
+ static RIGHT_DELIMITERS = [
1235
+ new _TypstToken(2 /* ELEMENT */, ")"),
1236
+ new _TypstToken(2 /* ELEMENT */, "]"),
1237
+ new _TypstToken(2 /* ELEMENT */, "}"),
1238
+ new _TypstToken(2 /* ELEMENT */, "|"),
1239
+ new _TypstToken(1 /* SYMBOL */, "angle.r")
1240
+ ];
1150
1241
  };
1242
+ var TypstWriterError = class extends Error {
1243
+ node;
1244
+ constructor(message, node) {
1245
+ super(message);
1246
+ this.name = "TypstWriterError";
1247
+ this.node = node;
1248
+ }
1249
+ };
1250
+ var SOFT_SPACE = new TypstToken(7 /* CONTROL */, " ");
1151
1251
  var TypstNode = class {
1152
1252
  type;
1153
1253
  head;
@@ -1178,6 +1278,39 @@ var TypstTerminal = class extends TypstNode {
1178
1278
  toString() {
1179
1279
  return this.head.toString();
1180
1280
  }
1281
+ serialize(env, options) {
1282
+ if (this.head.type === 2 /* ELEMENT */) {
1283
+ if (this.head.value === "," && env.insideFunctionDepth > 0) {
1284
+ return [new TypstToken(1 /* SYMBOL */, "comma")];
1285
+ }
1286
+ } else if (this.head.type === 1 /* SYMBOL */) {
1287
+ let symbol_name = this.head.value;
1288
+ if (options.preferShorthands) {
1289
+ if (shorthandMap.has(symbol_name)) {
1290
+ symbol_name = shorthandMap.get(symbol_name);
1291
+ }
1292
+ }
1293
+ if (options.inftyToOo && symbol_name === "infinity") {
1294
+ symbol_name = "oo";
1295
+ }
1296
+ return [new TypstToken(1 /* SYMBOL */, symbol_name)];
1297
+ } else if (this.head.type === 6 /* SPACE */ || this.head.type === 8 /* NEWLINE */) {
1298
+ const queue = [];
1299
+ for (const c of this.head.value) {
1300
+ if (c === " ") {
1301
+ if (options.keepSpaces) {
1302
+ queue.push(new TypstToken(6 /* SPACE */, c));
1303
+ }
1304
+ } else if (c === "\n") {
1305
+ queue.push(new TypstToken(1 /* SYMBOL */, c));
1306
+ } else {
1307
+ throw new TypstWriterError(`Unexpected whitespace character: ${c}`, this);
1308
+ }
1309
+ }
1310
+ return queue;
1311
+ }
1312
+ return [this.head];
1313
+ }
1181
1314
  };
1182
1315
  var TypstGroup = class extends TypstNode {
1183
1316
  items;
@@ -1188,6 +1321,16 @@ var TypstGroup = class extends TypstNode {
1188
1321
  isOverHigh() {
1189
1322
  return this.items.some((n) => n.isOverHigh());
1190
1323
  }
1324
+ serialize(env, options) {
1325
+ const queue = this.items.flatMap((n) => n.serialize(env, options));
1326
+ if (queue.length > 0 && queue[0].eq(SOFT_SPACE)) {
1327
+ queue.shift();
1328
+ }
1329
+ if (queue.length > 0 && queue[queue.length - 1].eq(SOFT_SPACE)) {
1330
+ queue.pop();
1331
+ }
1332
+ return queue;
1333
+ }
1191
1334
  };
1192
1335
  var TypstSupsub = class extends TypstNode {
1193
1336
  base;
@@ -1202,6 +1345,25 @@ var TypstSupsub = class extends TypstNode {
1202
1345
  isOverHigh() {
1203
1346
  return this.base.isOverHigh();
1204
1347
  }
1348
+ serialize(env, options) {
1349
+ const queue = [];
1350
+ let { base, sup, sub } = this;
1351
+ queue.push(...base.serialize(env, options));
1352
+ const has_prime = sup && sup.head.eq(new TypstToken(2 /* ELEMENT */, "'"));
1353
+ if (has_prime) {
1354
+ queue.push(new TypstToken(2 /* ELEMENT */, "'"));
1355
+ }
1356
+ if (sub) {
1357
+ queue.push(new TypstToken(2 /* ELEMENT */, "_"));
1358
+ queue.push(...sub.serialize(env, options));
1359
+ }
1360
+ if (sup && !has_prime) {
1361
+ queue.push(new TypstToken(2 /* ELEMENT */, "^"));
1362
+ queue.push(...sup.serialize(env, options));
1363
+ }
1364
+ queue.push(SOFT_SPACE);
1365
+ return queue;
1366
+ }
1205
1367
  };
1206
1368
  var TypstFuncCall = class extends TypstNode {
1207
1369
  args;
@@ -1215,6 +1377,27 @@ var TypstFuncCall = class extends TypstNode {
1215
1377
  }
1216
1378
  return this.args.some((n) => n.isOverHigh());
1217
1379
  }
1380
+ serialize(env, options) {
1381
+ const queue = [];
1382
+ const func_symbol = this.head;
1383
+ queue.push(func_symbol);
1384
+ env.insideFunctionDepth++;
1385
+ queue.push(TYPST_LEFT_PARENTHESIS);
1386
+ for (let i = 0; i < this.args.length; i++) {
1387
+ queue.push(...this.args[i].serialize(env, options));
1388
+ if (i < this.args.length - 1) {
1389
+ queue.push(new TypstToken(2 /* ELEMENT */, ","));
1390
+ }
1391
+ }
1392
+ if (this.options) {
1393
+ for (const [key, value] of Object.entries(this.options)) {
1394
+ queue.push(new TypstToken(3 /* LITERAL */, `, ${key}: ${value.toString()}`));
1395
+ }
1396
+ }
1397
+ queue.push(TYPST_RIGHT_PARENTHESIS);
1398
+ env.insideFunctionDepth--;
1399
+ return queue;
1400
+ }
1218
1401
  };
1219
1402
  var TypstFraction = class extends TypstNode {
1220
1403
  args;
@@ -1225,7 +1408,19 @@ var TypstFraction = class extends TypstNode {
1225
1408
  isOverHigh() {
1226
1409
  return true;
1227
1410
  }
1411
+ serialize(env, options) {
1412
+ const queue = [];
1413
+ const [numerator, denominator] = this.args;
1414
+ queue.push(SOFT_SPACE);
1415
+ queue.push(...numerator.serialize(env, options));
1416
+ queue.push(new TypstToken(2 /* ELEMENT */, "/"));
1417
+ queue.push(...denominator.serialize(env, options));
1418
+ queue.push(SOFT_SPACE);
1419
+ return queue;
1420
+ }
1228
1421
  };
1422
+ var TYPST_LEFT_PARENTHESIS = new TypstToken(2 /* ELEMENT */, "(");
1423
+ var TYPST_RIGHT_PARENTHESIS = new TypstToken(2 /* ELEMENT */, ")");
1229
1424
  var TypstLeftright = class extends TypstNode {
1230
1425
  body;
1231
1426
  left;
@@ -1240,8 +1435,28 @@ var TypstLeftright = class extends TypstNode {
1240
1435
  isOverHigh() {
1241
1436
  return this.body.isOverHigh();
1242
1437
  }
1438
+ serialize(env, options) {
1439
+ const queue = [];
1440
+ const LR = new TypstToken(1 /* SYMBOL */, "lr");
1441
+ const { left, right } = this;
1442
+ if (this.head.eq(LR)) {
1443
+ queue.push(LR);
1444
+ queue.push(TYPST_LEFT_PARENTHESIS);
1445
+ }
1446
+ if (left) {
1447
+ queue.push(left);
1448
+ }
1449
+ queue.push(...this.body.serialize(env, options));
1450
+ if (right) {
1451
+ queue.push(right);
1452
+ }
1453
+ if (this.head.eq(LR)) {
1454
+ queue.push(TYPST_RIGHT_PARENTHESIS);
1455
+ }
1456
+ return queue;
1457
+ }
1243
1458
  };
1244
- var TypstMatrixLike = class extends TypstNode {
1459
+ var TypstMatrixLike = class _TypstMatrixLike extends TypstNode {
1245
1460
  matrix;
1246
1461
  // head is 'mat', 'cases' or null
1247
1462
  constructor(head, data) {
@@ -1251,6 +1466,48 @@ var TypstMatrixLike = class extends TypstNode {
1251
1466
  isOverHigh() {
1252
1467
  return true;
1253
1468
  }
1469
+ serialize(env, options) {
1470
+ const queue = [];
1471
+ let cell_sep;
1472
+ let row_sep;
1473
+ if (this.head.eq(_TypstMatrixLike.MAT)) {
1474
+ cell_sep = new TypstToken(2 /* ELEMENT */, ",");
1475
+ row_sep = new TypstToken(2 /* ELEMENT */, ";");
1476
+ } else if (this.head.eq(_TypstMatrixLike.CASES)) {
1477
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1478
+ row_sep = new TypstToken(2 /* ELEMENT */, ",");
1479
+ } else if (this.head.eq(TypstToken.NONE)) {
1480
+ cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1481
+ row_sep = new TypstToken(1 /* SYMBOL */, "\\");
1482
+ }
1483
+ if (!this.head.eq(TypstToken.NONE)) {
1484
+ queue.push(this.head);
1485
+ env.insideFunctionDepth++;
1486
+ queue.push(TYPST_LEFT_PARENTHESIS);
1487
+ if (this.options) {
1488
+ for (const [key, value] of Object.entries(this.options)) {
1489
+ queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1490
+ }
1491
+ }
1492
+ }
1493
+ this.matrix.forEach((row, i) => {
1494
+ row.forEach((cell, j) => {
1495
+ queue.push(...cell.serialize(env, options));
1496
+ if (j < row.length - 1) {
1497
+ queue.push(cell_sep);
1498
+ } else {
1499
+ if (i < this.matrix.length - 1) {
1500
+ queue.push(row_sep);
1501
+ }
1502
+ }
1503
+ });
1504
+ });
1505
+ if (!this.head.eq(TypstToken.NONE)) {
1506
+ queue.push(TYPST_RIGHT_PARENTHESIS);
1507
+ env.insideFunctionDepth--;
1508
+ }
1509
+ return queue;
1510
+ }
1254
1511
  static MAT = new TypstToken(1 /* SYMBOL */, "mat");
1255
1512
  static CASES = new TypstToken(1 /* SYMBOL */, "cases");
1256
1513
  };
@@ -1270,88 +1527,45 @@ var TypstMarkupFunc = class extends TypstNode {
1270
1527
  isOverHigh() {
1271
1528
  return this.fragments.some((n) => n.isOverHigh());
1272
1529
  }
1273
- };
1274
-
1275
- // src/typst-shorthands.ts
1276
- var shorthandMap = /* @__PURE__ */ new Map([
1277
- ["arrow.l.r.double.long", "<==>"],
1278
- ["arrow.l.r.long", "<-->"],
1279
- ["arrow.r.bar", "|->"],
1280
- ["arrow.r.double.bar", "|=>"],
1281
- ["arrow.r.double.long", "==>"],
1282
- ["arrow.r.long", "-->"],
1283
- ["arrow.r.long.squiggly", "~~>"],
1284
- ["arrow.r.tail", ">->"],
1285
- ["arrow.r.twohead", "->>"],
1286
- ["arrow.l.double.long", "<=="],
1287
- ["arrow.l.long", "<--"],
1288
- ["arrow.l.long.squiggly", "<~~"],
1289
- ["arrow.l.tail", "<-<"],
1290
- ["arrow.l.twohead", "<<-"],
1291
- ["arrow.l.r", "<->"],
1292
- ["arrow.l.r.double", "<=>"],
1293
- ["colon.double.eq", "::="],
1294
- ["dots.h", "..."],
1295
- ["gt.triple", ">>>"],
1296
- ["lt.triple", "<<<"],
1297
- ["arrow.r", "->"],
1298
- ["arrow.r.double", "=>"],
1299
- ["arrow.r.squiggly", "~>"],
1300
- ["arrow.l", "<-"],
1301
- ["arrow.l.squiggly", "<~"],
1302
- ["bar.v.double", "||"],
1303
- ["bracket.l.double", "[|"],
1304
- ["bracket.r.double", "|]"],
1305
- ["colon.eq", ":="],
1306
- ["eq.colon", "=:"],
1307
- ["eq.not", "!="],
1308
- ["gt.double", ">>"],
1309
- ["gt.eq", ">="],
1310
- ["lt.double", "<<"],
1311
- ["lt.eq", "<="],
1312
- ["ast.op", "*"],
1313
- ["minus", "-"],
1314
- ["tilde.op", "~"]
1315
- ]);
1316
- var reverseShorthandMap = /* @__PURE__ */ new Map();
1317
- for (const [key, value] of shorthandMap.entries()) {
1318
- if (value.length > 1) {
1319
- reverseShorthandMap.set(value, key);
1530
+ serialize(env, options) {
1531
+ const queue = [];
1532
+ queue.push(this.head);
1533
+ env.insideFunctionDepth++;
1534
+ queue.push(TYPST_LEFT_PARENTHESIS);
1535
+ if (this.options) {
1536
+ const entries = Object.entries(this.options);
1537
+ for (let i = 0; i < entries.length; i++) {
1538
+ const [key, value] = entries[i];
1539
+ queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}`));
1540
+ if (i < entries.length - 1) {
1541
+ queue.push(new TypstToken(2 /* ELEMENT */, ","));
1542
+ }
1543
+ }
1544
+ }
1545
+ queue.push(TYPST_RIGHT_PARENTHESIS);
1546
+ queue.push(new TypstToken(3 /* LITERAL */, "["));
1547
+ for (const frag of this.fragments) {
1548
+ queue.push(new TypstToken(3 /* LITERAL */, "$"));
1549
+ queue.push(...frag.serialize(env, options));
1550
+ queue.push(new TypstToken(3 /* LITERAL */, "$"));
1551
+ }
1552
+ queue.push(new TypstToken(3 /* LITERAL */, "]"));
1553
+ return queue;
1320
1554
  }
1321
- }
1555
+ };
1322
1556
 
1323
1557
  // src/typst-writer.ts
1324
- function is_delimiter(c) {
1325
- return c.head.type === 2 /* ELEMENT */ && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.head.value);
1326
- }
1327
- var TYPST_LEFT_PARENTHESIS = new TypstToken(2 /* ELEMENT */, "(");
1328
- var TYPST_RIGHT_PARENTHESIS = new TypstToken(2 /* ELEMENT */, ")");
1558
+ var TYPST_LEFT_PARENTHESIS2 = new TypstToken(2 /* ELEMENT */, "(");
1559
+ var TYPST_RIGHT_PARENTHESIS2 = new TypstToken(2 /* ELEMENT */, ")");
1329
1560
  var TYPST_COMMA = new TypstToken(2 /* ELEMENT */, ",");
1330
1561
  var TYPST_NEWLINE = new TypstToken(1 /* SYMBOL */, "\n");
1331
- var SOFT_SPACE = new TypstToken(7 /* CONTROL */, " ");
1332
- var TypstWriterError = class extends Error {
1333
- node;
1334
- constructor(message, node) {
1335
- super(message);
1336
- this.name = "TypstWriterError";
1337
- this.node = node;
1338
- }
1339
- };
1562
+ var SOFT_SPACE2 = new TypstToken(7 /* CONTROL */, " ");
1340
1563
  var TypstWriter = class {
1341
- nonStrict;
1342
- preferShorthands;
1343
- keepSpaces;
1344
- inftyToOo;
1345
- optimize;
1346
1564
  buffer = "";
1347
1565
  queue = [];
1348
- insideFunctionDepth = 0;
1566
+ options;
1349
1567
  constructor(options) {
1350
- this.nonStrict = options.nonStrict;
1351
- this.preferShorthands = options.preferShorthands;
1352
- this.keepSpaces = options.keepSpaces;
1353
- this.inftyToOo = options.inftyToOo;
1354
- this.optimize = options.optimize;
1568
+ this.options = options;
1355
1569
  }
1356
1570
  writeBuffer(previousToken, token) {
1357
1571
  const str = token.toString();
@@ -1367,7 +1581,7 @@ var TypstWriter = class {
1367
1581
  no_need_space ||= /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+";
1368
1582
  no_need_space ||= str.startsWith("\n");
1369
1583
  no_need_space ||= this.buffer === "";
1370
- no_need_space ||= /^\s/.test(str);
1584
+ no_need_space ||= /\s$/.test(this.buffer) || /^\s/.test(str);
1371
1585
  no_need_space ||= this.buffer.endsWith("&") && str === "=";
1372
1586
  no_need_space ||= this.buffer.endsWith("/") || str === "/";
1373
1587
  no_need_space ||= token.type === 3 /* LITERAL */;
@@ -1382,239 +1596,31 @@ var TypstWriter = class {
1382
1596
  }
1383
1597
  // Serialize a tree of TypstNode into a list of TypstToken
1384
1598
  serialize(abstractNode) {
1385
- switch (abstractNode.type) {
1386
- case "terminal": {
1387
- const node = abstractNode;
1388
- if (node.head.type === 2 /* ELEMENT */) {
1389
- if (node.head.value === "," && this.insideFunctionDepth > 0) {
1390
- this.queue.push(new TypstToken(1 /* SYMBOL */, "comma"));
1391
- } else {
1392
- this.queue.push(node.head);
1393
- }
1394
- break;
1395
- } else if (node.head.type === 1 /* SYMBOL */) {
1396
- let symbol_name = node.head.value;
1397
- if (this.preferShorthands) {
1398
- if (shorthandMap.has(symbol_name)) {
1399
- symbol_name = shorthandMap.get(symbol_name);
1400
- }
1401
- }
1402
- if (this.inftyToOo && symbol_name === "infinity") {
1403
- symbol_name = "oo";
1404
- }
1405
- this.queue.push(new TypstToken(1 /* SYMBOL */, symbol_name));
1406
- break;
1407
- } else if (node.head.type === 6 /* SPACE */ || node.head.type === 8 /* NEWLINE */) {
1408
- for (const c of node.head.value) {
1409
- if (c === " ") {
1410
- if (this.keepSpaces) {
1411
- this.queue.push(new TypstToken(6 /* SPACE */, c));
1412
- }
1413
- } else if (c === "\n") {
1414
- this.queue.push(new TypstToken(1 /* SYMBOL */, c));
1415
- } else {
1416
- throw new TypstWriterError(`Unexpected whitespace character: ${c}`, node);
1417
- }
1418
- }
1419
- break;
1420
- } else {
1421
- this.queue.push(node.head);
1422
- break;
1423
- }
1424
- }
1425
- case "group": {
1426
- const node = abstractNode;
1427
- for (const item of node.items) {
1428
- this.serialize(item);
1429
- }
1430
- break;
1431
- }
1432
- case "leftright": {
1433
- const node = abstractNode;
1434
- const LR = new TypstToken(1 /* SYMBOL */, "lr");
1435
- const { left, right } = node;
1436
- if (node.head.eq(LR)) {
1437
- this.queue.push(LR);
1438
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1439
- }
1440
- if (left) {
1441
- this.queue.push(left);
1442
- }
1443
- this.serialize(node.body);
1444
- if (right) {
1445
- this.queue.push(right);
1446
- }
1447
- if (node.head.eq(LR)) {
1448
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1449
- }
1450
- break;
1451
- }
1452
- case "supsub": {
1453
- const node = abstractNode;
1454
- let { base, sup, sub } = node;
1455
- this.appendWithBracketsIfNeeded(base);
1456
- let trailing_space_needed = false;
1457
- const has_prime = sup && sup.head.eq(new TypstToken(2 /* ELEMENT */, "'"));
1458
- if (has_prime) {
1459
- this.queue.push(new TypstToken(2 /* ELEMENT */, "'"));
1460
- trailing_space_needed = false;
1461
- }
1462
- if (sub) {
1463
- this.queue.push(new TypstToken(2 /* ELEMENT */, "_"));
1464
- trailing_space_needed = this.appendWithBracketsIfNeeded(sub);
1465
- }
1466
- if (sup && !has_prime) {
1467
- this.queue.push(new TypstToken(2 /* ELEMENT */, "^"));
1468
- trailing_space_needed = this.appendWithBracketsIfNeeded(sup);
1469
- }
1470
- if (trailing_space_needed) {
1471
- this.queue.push(SOFT_SPACE);
1472
- }
1473
- break;
1474
- }
1475
- case "funcCall": {
1476
- const node = abstractNode;
1477
- const func_symbol = node.head;
1478
- this.queue.push(func_symbol);
1479
- this.insideFunctionDepth++;
1480
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1481
- for (let i = 0; i < node.args.length; i++) {
1482
- this.serialize(node.args[i]);
1483
- if (i < node.args.length - 1) {
1484
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1485
- }
1486
- }
1487
- if (node.options) {
1488
- for (const [key, value] of Object.entries(node.options)) {
1489
- this.queue.push(new TypstToken(3 /* LITERAL */, `, ${key}: ${value.toString()}`));
1490
- }
1491
- }
1492
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1493
- this.insideFunctionDepth--;
1494
- break;
1495
- }
1496
- case "fraction": {
1497
- const node = abstractNode;
1498
- const [numerator, denominator] = node.args;
1499
- const pos = this.queue.length;
1500
- const no_wrap = this.appendWithBracketsIfNeeded(numerator);
1501
- const wrapped = !no_wrap;
1502
- if (wrapped) {
1503
- this.queue.splice(pos, 0, SOFT_SPACE);
1504
- }
1505
- this.queue.push(new TypstToken(2 /* ELEMENT */, "/"));
1506
- this.appendWithBracketsIfNeeded(denominator);
1507
- break;
1508
- }
1509
- case "matrixLike": {
1510
- const node = abstractNode;
1511
- const matrix = node.matrix;
1512
- let cell_sep;
1513
- let row_sep;
1514
- if (node.head.eq(TypstMatrixLike.MAT)) {
1515
- cell_sep = new TypstToken(2 /* ELEMENT */, ",");
1516
- row_sep = new TypstToken(2 /* ELEMENT */, ";");
1517
- } else if (node.head.eq(TypstMatrixLike.CASES)) {
1518
- cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1519
- row_sep = new TypstToken(2 /* ELEMENT */, ",");
1520
- } else if (node.head.eq(TypstToken.NONE)) {
1521
- cell_sep = new TypstToken(2 /* ELEMENT */, "&");
1522
- row_sep = new TypstToken(1 /* SYMBOL */, "\\");
1523
- }
1524
- if (!node.head.eq(TypstToken.NONE)) {
1525
- this.queue.push(node.head);
1526
- this.insideFunctionDepth++;
1527
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1528
- if (node.options) {
1529
- for (const [key, value] of Object.entries(node.options)) {
1530
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}, `));
1531
- }
1532
- }
1533
- }
1534
- matrix.forEach((row, i) => {
1535
- row.forEach((cell, j) => {
1536
- this.serialize(cell);
1537
- if (j < row.length - 1) {
1538
- this.queue.push(cell_sep);
1539
- } else {
1540
- if (i < matrix.length - 1) {
1541
- this.queue.push(row_sep);
1542
- }
1543
- }
1544
- });
1545
- });
1546
- if (!node.head.eq(TypstToken.NONE)) {
1547
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1548
- this.insideFunctionDepth--;
1549
- }
1550
- break;
1551
- }
1552
- case "markupFunc": {
1553
- const node = abstractNode;
1554
- this.queue.push(node.head);
1555
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1556
- if (node.options) {
1557
- const entries = Object.entries(node.options);
1558
- for (let i = 0; i < entries.length; i++) {
1559
- const [key, value] = entries[i];
1560
- this.queue.push(new TypstToken(3 /* LITERAL */, `${key}: ${value.toString()}`));
1561
- if (i < entries.length - 1) {
1562
- this.queue.push(new TypstToken(2 /* ELEMENT */, ","));
1563
- }
1564
- }
1565
- }
1566
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1567
- this.queue.push(new TypstToken(3 /* LITERAL */, "["));
1568
- for (const frag of node.fragments) {
1569
- this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1570
- this.serialize(frag);
1571
- this.queue.push(new TypstToken(3 /* LITERAL */, "$"));
1572
- }
1573
- this.queue.push(new TypstToken(3 /* LITERAL */, "]"));
1574
- break;
1575
- }
1576
- default:
1577
- throw new TypstWriterError(`Unimplemented node type to append: ${abstractNode.type}`, abstractNode);
1578
- }
1599
+ const env = { insideFunctionDepth: 0 };
1600
+ this.queue.push(...abstractNode.serialize(env, this.options));
1579
1601
  }
1580
- appendWithBracketsIfNeeded(node) {
1581
- let need_to_wrap = ["group", "supsub", "matrixLike", "fraction", "empty"].includes(node.type);
1582
- if (node.type === "group") {
1583
- const group = node;
1584
- if (group.items.length === 0) {
1585
- need_to_wrap = true;
1586
- } else {
1587
- const first = group.items[0];
1588
- const last = group.items[group.items.length - 1];
1589
- if (is_delimiter(first) && is_delimiter(last)) {
1590
- need_to_wrap = false;
1591
- }
1602
+ flushQueue() {
1603
+ let qu = [];
1604
+ for (const token of this.queue) {
1605
+ if (token.eq(SOFT_SPACE2) && qu.length > 0 && qu[qu.length - 1].eq(SOFT_SPACE2)) {
1606
+ continue;
1592
1607
  }
1608
+ qu.push(token);
1593
1609
  }
1594
- if (need_to_wrap) {
1595
- this.queue.push(TYPST_LEFT_PARENTHESIS);
1596
- this.serialize(node);
1597
- this.queue.push(TYPST_RIGHT_PARENTHESIS);
1598
- } else {
1599
- this.serialize(node);
1600
- }
1601
- return !need_to_wrap;
1602
- }
1603
- flushQueue() {
1604
1610
  const dummy_token = new TypstToken(1 /* SYMBOL */, "");
1605
- for (let i = 0; i < this.queue.length; i++) {
1606
- let token = this.queue[i];
1607
- if (token.eq(SOFT_SPACE)) {
1608
- const to_delete = i === 0 || i === this.queue.length - 1 || this.queue[i - 1].type === 6 /* SPACE */ || this.queue[i - 1].isOneOf([TYPST_LEFT_PARENTHESIS, TYPST_NEWLINE]) || this.queue[i + 1].isOneOf([TYPST_RIGHT_PARENTHESIS, TYPST_COMMA, TYPST_NEWLINE]);
1611
+ for (let i = 0; i < qu.length; i++) {
1612
+ let token = qu[i];
1613
+ if (token.eq(SOFT_SPACE2)) {
1614
+ const to_delete = i === 0 || i === qu.length - 1 || qu[i - 1].type === 6 /* SPACE */ || qu[i - 1].isOneOf([TYPST_LEFT_PARENTHESIS2, TYPST_NEWLINE]) || qu[i + 1].isOneOf([TYPST_RIGHT_PARENTHESIS2, TYPST_COMMA, TYPST_NEWLINE]);
1609
1615
  if (to_delete) {
1610
- this.queue[i] = dummy_token;
1616
+ qu[i] = dummy_token;
1611
1617
  }
1612
1618
  }
1613
1619
  }
1614
- this.queue = this.queue.filter((token) => !token.eq(dummy_token));
1615
- for (let i = 0; i < this.queue.length; i++) {
1616
- let token = this.queue[i];
1617
- let previous_token = i === 0 ? null : this.queue[i - 1];
1620
+ qu = qu.filter((token) => !token.eq(dummy_token));
1621
+ for (let i = 0; i < qu.length; i++) {
1622
+ let token = qu[i];
1623
+ let previous_token = i === 0 ? null : qu[i - 1];
1618
1624
  this.writeBuffer(previous_token, token);
1619
1625
  }
1620
1626
  this.queue = [];
@@ -1636,7 +1642,7 @@ var TypstWriter = class {
1636
1642
  res = res.replace(/round\(\)/g, 'round("")');
1637
1643
  return res;
1638
1644
  };
1639
- if (this.optimize) {
1645
+ if (this.options.optimize) {
1640
1646
  const all_passes = [smartFloorPass, smartCeilPass, smartRoundPass];
1641
1647
  for (const pass of all_passes) {
1642
1648
  this.buffer = pass(this.buffer);
@@ -1706,6 +1712,7 @@ var symbolMap = /* @__PURE__ */ new Map([
1706
1712
  ["mathbb", "bb"],
1707
1713
  ["mathbf", "bold"],
1708
1714
  ["mathcal", "cal"],
1715
+ ["mathscr", "scr"],
1709
1716
  ["mathit", "italic"],
1710
1717
  ["mathfrak", "frak"],
1711
1718
  ["mathrm", "upright"],
@@ -1906,7 +1913,6 @@ var symbolMap = /* @__PURE__ */ new Map([
1906
1913
  ["zeta", "zeta"],
1907
1914
  ["intop", "limits(integral)"],
1908
1915
  // extended
1909
- ["mathscr", "scr"],
1910
1916
  ["LaTeX", "#LaTeX"],
1911
1917
  ["TeX", "#TeX"]
1912
1918
  ]);
@@ -2874,6 +2880,35 @@ function convert_tex_array_align_literal(alignLiteral) {
2874
2880
  }
2875
2881
  return np;
2876
2882
  }
2883
+ var TYPST_LEFT_PARENTHESIS3 = new TypstToken(2 /* ELEMENT */, "(");
2884
+ var TYPST_RIGHT_PARENTHESIS3 = new TypstToken(2 /* ELEMENT */, ")");
2885
+ function is_delimiter(c) {
2886
+ return c.head.type === 2 /* ELEMENT */ && ["(", ")", "[", "]", "{", "}", "|", "\u230A", "\u230B", "\u2308", "\u2309"].includes(c.head.value);
2887
+ }
2888
+ function appendWithBracketsIfNeeded(node) {
2889
+ let need_to_wrap = ["group", "supsub", "matrixLike", "fraction", "empty"].includes(node.type);
2890
+ if (node.type === "group") {
2891
+ const group = node;
2892
+ if (group.items.length === 0) {
2893
+ need_to_wrap = true;
2894
+ } else {
2895
+ const first = group.items[0];
2896
+ const last = group.items[group.items.length - 1];
2897
+ if (is_delimiter(first) && is_delimiter(last)) {
2898
+ need_to_wrap = false;
2899
+ }
2900
+ }
2901
+ }
2902
+ if (need_to_wrap) {
2903
+ return new TypstLeftright(null, {
2904
+ left: TYPST_LEFT_PARENTHESIS3,
2905
+ right: TYPST_RIGHT_PARENTHESIS3,
2906
+ body: node
2907
+ });
2908
+ } else {
2909
+ return node;
2910
+ }
2911
+ }
2877
2912
  function convert_tex_node_to_typst(abstractNode, options = {}) {
2878
2913
  switch (abstractNode.type) {
2879
2914
  case "terminal": {
@@ -2882,12 +2917,6 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2882
2917
  }
2883
2918
  case "text": {
2884
2919
  const node2 = abstractNode;
2885
- if (/[^\x00-\x7F]+/.test(node2.head.value) && options.nonAsciiWrapper !== "") {
2886
- return new TypstFuncCall(
2887
- new TypstToken(1 /* SYMBOL */, options.nonAsciiWrapper),
2888
- [new TypstToken(4 /* TEXT */, node2.head.value).toNode()]
2889
- );
2890
- }
2891
2920
  return new TypstToken(4 /* TEXT */, node2.head.value).toNode();
2892
2921
  }
2893
2922
  case "ordgroup":
@@ -2914,6 +2943,13 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
2914
2943
  sup: sup ? convert_tex_node_to_typst(sup, options) : null,
2915
2944
  sub: sub ? convert_tex_node_to_typst(sub, options) : null
2916
2945
  };
2946
+ data.base = appendWithBracketsIfNeeded(data.base);
2947
+ if (data.sup) {
2948
+ data.sup = appendWithBracketsIfNeeded(data.sup);
2949
+ }
2950
+ if (data.sub) {
2951
+ data.sub = appendWithBracketsIfNeeded(data.sub);
2952
+ }
2917
2953
  return new TypstSupsub(data);
2918
2954
  }
2919
2955
  case "leftright": {
@@ -3010,6 +3046,36 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
3010
3046
  if (node2.head.value === "\\substack") {
3011
3047
  return arg0;
3012
3048
  }
3049
+ if (node2.head.value === "\\displaylines") {
3050
+ return arg0;
3051
+ }
3052
+ if (node2.head.value === "\\mathinner") {
3053
+ return arg0;
3054
+ }
3055
+ if (node2.head.value === "\\mathrel") {
3056
+ return new TypstFuncCall(
3057
+ new TypstToken(1 /* SYMBOL */, "class"),
3058
+ [new TypstToken(4 /* TEXT */, "relation").toNode(), arg0]
3059
+ );
3060
+ }
3061
+ if (node2.head.value === "\\mathbin") {
3062
+ return new TypstFuncCall(
3063
+ new TypstToken(1 /* SYMBOL */, "class"),
3064
+ [new TypstToken(4 /* TEXT */, "binary").toNode(), arg0]
3065
+ );
3066
+ }
3067
+ if (node2.head.value === "\\mathop") {
3068
+ return new TypstFuncCall(
3069
+ new TypstToken(1 /* SYMBOL */, "class"),
3070
+ [new TypstToken(4 /* TEXT */, "large").toNode(), arg0]
3071
+ );
3072
+ }
3073
+ if (node2.head.value === "\\set") {
3074
+ return new TypstLeftright(
3075
+ null,
3076
+ { body: arg0, left: TypstToken.LEFT_BRACE, right: TypstToken.RIGHT_BRACE }
3077
+ );
3078
+ }
3013
3079
  if (node2.head.value === "\\overset") {
3014
3080
  return convert_overset(node2, options);
3015
3081
  }
@@ -3018,7 +3084,7 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
3018
3084
  }
3019
3085
  if (node2.head.value === "\\frac") {
3020
3086
  if (options.fracToSlash) {
3021
- return new TypstFraction(node2.args.map((n) => convert_tex_node_to_typst(n, options)));
3087
+ return new TypstFraction(node2.args.map((n) => convert_tex_node_to_typst(n, options)).map(appendWithBracketsIfNeeded));
3022
3088
  }
3023
3089
  }
3024
3090
  if (options.optimize) {
@@ -3282,6 +3348,31 @@ function convert_typst_node_to_tex(abstractNode) {
3282
3348
  assert(arg0.head.type === 4 /* TEXT */);
3283
3349
  return new TexFuncCall(typst_token_to_tex(node.head), [new TexToken(3 /* LITERAL */, arg0.head.value).toNode()]);
3284
3350
  }
3351
+ case "class": {
3352
+ const arg0 = node.args[0];
3353
+ assert(arg0.head.type === 4 /* TEXT */);
3354
+ let command;
3355
+ switch (arg0.head.value) {
3356
+ // \mathrel{X} <- class("relation", X)
3357
+ case "relation":
3358
+ command = "\\mathrel";
3359
+ break;
3360
+ // \mathbin{X} <- class("binary", X)
3361
+ case "binary":
3362
+ command = "\\mathbin";
3363
+ break;
3364
+ // \mathop{X} <- class("large", X)
3365
+ case "large":
3366
+ command = "\\mathop";
3367
+ break;
3368
+ default:
3369
+ throw new Error(`Unimplemented class: ${arg0.head.value}`);
3370
+ }
3371
+ return new TexFuncCall(
3372
+ new TexToken(2 /* COMMAND */, command),
3373
+ [convert_typst_node_to_tex(node.args[1])]
3374
+ );
3375
+ }
3285
3376
  // general case
3286
3377
  default: {
3287
3378
  const func_name_tex = typst_token_to_tex(node.head);
@@ -3499,7 +3590,7 @@ function _find_closing_match(tokens, start, leftBrackets, rightBrackets) {
3499
3590
  }
3500
3591
  return pos - 1;
3501
3592
  }
3502
- function find_closing_match2(tokens, start) {
3593
+ function find_closing_match(tokens, start) {
3503
3594
  return _find_closing_match(
3504
3595
  tokens,
3505
3596
  start,
@@ -3511,8 +3602,8 @@ function find_closing_delim(tokens, start) {
3511
3602
  return _find_closing_match(
3512
3603
  tokens,
3513
3604
  start,
3514
- [LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2, VERTICAL_BAR],
3515
- [RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET2, VERTICAL_BAR]
3605
+ TypstToken.LEFT_DELIMITERS,
3606
+ TypstToken.RIGHT_DELIMITERS
3516
3607
  );
3517
3608
  }
3518
3609
  function find_closing_parenthesis(nodes, start) {
@@ -3645,7 +3736,6 @@ var LEFT_BRACKET = new TypstToken(2 /* ELEMENT */, "[");
3645
3736
  var RIGHT_BRACKET = new TypstToken(2 /* ELEMENT */, "]");
3646
3737
  var LEFT_CURLY_BRACKET2 = new TypstToken(2 /* ELEMENT */, "{");
3647
3738
  var RIGHT_CURLY_BRACKET2 = new TypstToken(2 /* ELEMENT */, "}");
3648
- var VERTICAL_BAR = new TypstToken(2 /* ELEMENT */, "|");
3649
3739
  var COMMA = new TypstToken(2 /* ELEMENT */, ",");
3650
3740
  var SEMICOLON = new TypstToken(2 /* ELEMENT */, ";");
3651
3741
  var SINGLE_SPACE = new TypstToken(6 /* SPACE */, " ");
@@ -3720,7 +3810,7 @@ var TypstParser = class {
3720
3810
  let node;
3721
3811
  let end;
3722
3812
  if (tokens[start].eq(LEFT_PARENTHESES)) {
3723
- const pos_closing = find_closing_match2(tokens, start);
3813
+ const pos_closing = find_closing_match(tokens, start);
3724
3814
  [node, end] = this.parseGroup(tokens, start + 1, pos_closing);
3725
3815
  } else {
3726
3816
  [node, end] = this.parseNextExprWithoutSupSub(tokens, start);
@@ -3736,7 +3826,7 @@ var TypstParser = class {
3736
3826
  const firstToken = tokens[start];
3737
3827
  const node = firstToken.toNode();
3738
3828
  if (firstToken.eq(LEFT_PARENTHESES)) {
3739
- const pos_closing = find_closing_match2(tokens, start);
3829
+ const pos_closing = find_closing_match(tokens, start);
3740
3830
  return this.parseGroup(tokens, start + 1, pos_closing, true);
3741
3831
  }
3742
3832
  if (firstToken.type === 2 /* ELEMENT */ && !isalpha(firstToken.value[0])) {
@@ -3780,32 +3870,32 @@ var TypstParser = class {
3780
3870
  }
3781
3871
  // start: the position of the left parentheses
3782
3872
  parseArguments(tokens, start) {
3783
- const end = find_closing_match2(tokens, start);
3873
+ const end = find_closing_match(tokens, start);
3784
3874
  return [this.parseArgumentsWithSeparator(tokens, start + 1, end, COMMA), end + 1];
3785
3875
  }
3786
3876
  // start: the position of the left parentheses
3787
3877
  parseLrArguments(tokens, start) {
3788
3878
  const lr_token = tokens[start];
3789
- if (tokens[start + 1].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET2, VERTICAL_BAR])) {
3790
- const end = find_closing_match2(tokens, start);
3879
+ const end = find_closing_match(tokens, start);
3880
+ if (tokens[start + 1].isOneOf(TypstToken.LEFT_DELIMITERS)) {
3791
3881
  const inner_start = start + 1;
3792
3882
  const inner_end = find_closing_delim(tokens, inner_start);
3793
- const inner_args = this.parseArgumentsWithSeparator(tokens, inner_start + 1, inner_end, COMMA);
3883
+ const [inner_args, _] = this.parseGroup(tokens, inner_start + 1, inner_end);
3794
3884
  return [
3795
- new TypstLeftright(lr_token, { body: new TypstGroup(inner_args), left: tokens[inner_start], right: tokens[inner_end] }),
3885
+ new TypstLeftright(lr_token, { body: inner_args, left: tokens[inner_start], right: tokens[inner_end] }),
3796
3886
  end + 1
3797
3887
  ];
3798
3888
  } else {
3799
- const [args, end] = this.parseArguments(tokens, start);
3889
+ const [inner_args, _] = this.parseGroup(tokens, start + 1, end - 1);
3800
3890
  return [
3801
- new TypstLeftright(lr_token, { body: new TypstGroup(args), left: null, right: null }),
3802
- end
3891
+ new TypstLeftright(lr_token, { body: inner_args, left: null, right: null }),
3892
+ end + 1
3803
3893
  ];
3804
3894
  }
3805
3895
  }
3806
3896
  // start: the position of the left parentheses
3807
3897
  parseMatrix(tokens, start, rowSepToken, cellSepToken) {
3808
- const end = find_closing_match2(tokens, start);
3898
+ const end = find_closing_match(tokens, start);
3809
3899
  tokens = tokens.slice(0, end);
3810
3900
  const matrix = [];
3811
3901
  let named_params = {};
@@ -3919,12 +4009,14 @@ function tex2typst(tex, options) {
3919
4009
  fracToSlash: true,
3920
4010
  inftyToOo: false,
3921
4011
  optimize: true,
3922
- nonAsciiWrapper: "",
3923
4012
  customTexMacros: {}
3924
4013
  };
3925
4014
  if (options !== void 0) {
4015
+ if (typeof options !== "object") {
4016
+ throw new Error("options must be an object");
4017
+ }
3926
4018
  for (const key in opt) {
3927
- if (options[key] !== void 0) {
4019
+ if (key in options) {
3928
4020
  opt[key] = options[key];
3929
4021
  }
3930
4022
  }