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/README.md +1 -1
- package/dist/index.d.ts +22 -24
- package/dist/index.js +509 -417
- package/dist/parser.js +23 -0
- package/dist/tex2typst.min.js +13 -13
- package/package.json +1 -1
- package/src/convert.ts +112 -8
- package/src/exposed-types.ts +22 -24
- package/src/generic.ts +16 -0
- package/src/index.ts +7 -4
- package/src/jslex.ts +1 -1
- package/src/map.ts +1 -1
- package/src/tex-parser.ts +128 -107
- package/src/tex-tokenizer.ts +6 -0
- package/src/tex2typst.ts +2 -4
- package/src/typst-parser.ts +9 -10
- package/src/typst-types.ts +484 -230
- package/src/typst-writer.ts +28 -274
- package/tests/cheat-sheet.test.ts +42 -0
- package/tests/cheat-sheet.toml +304 -0
- package/tests/example.ts +15 -0
- package/tests/general-symbols.test.ts +22 -0
- package/tests/general-symbols.toml +755 -0
- package/tests/integration-tex2typst.yaml +89 -0
- package/tests/struct-bidirection.yaml +188 -0
- package/tests/struct-tex2typst.yaml +463 -0
- package/tests/struct-typst2tex.yaml +412 -0
- package/tests/symbol.yml +126 -0
- package/tests/test-common.ts +26 -0
- package/tests/tex-parser.test.ts +97 -0
- package/tests/tex-to-typst.test.ts +136 -0
- package/tests/typst-parser.test.ts +134 -0
- package/tests/typst-to-tex.test.ts +76 -0
- package/tsconfig.json +4 -4
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
|
-
|
|
748
|
-
|
|
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
|
-
|
|
770
|
-
return tree;
|
|
766
|
+
return this.parseGroup(tokens.slice(0));
|
|
771
767
|
} else if (idx === 0) {
|
|
772
|
-
const
|
|
768
|
+
const tree = this.parseGroup(tokens.slice(1));
|
|
773
769
|
return new TexFuncCall(token_displaystyle, [tree]);
|
|
774
770
|
} else {
|
|
775
|
-
const
|
|
776
|
-
const
|
|
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
|
|
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 <
|
|
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,
|
|
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
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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
|
-
|
|
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
|
|
888
|
-
if (
|
|
889
|
-
throw
|
|
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
|
|
903
|
+
return [group, newPos];
|
|
892
904
|
case "}":
|
|
893
|
-
throw
|
|
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
|
|
932
|
-
|
|
933
|
-
|
|
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 [
|
|
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
|
|
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
|
|
1016
|
+
throw LatexParserError.UNMATCHED_COMMAND_LEFT;
|
|
1000
1017
|
}
|
|
1001
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1048
|
+
throw LatexParserError.UNMATCHED_COMMAND_BEGIN;
|
|
1036
1049
|
}
|
|
1037
|
-
|
|
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("
|
|
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
|
-
|
|
1055
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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
|
-
|
|
1325
|
-
|
|
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
|
|
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
|
-
|
|
1566
|
+
options;
|
|
1349
1567
|
constructor(options) {
|
|
1350
|
-
this.
|
|
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
|
-
|
|
1386
|
-
|
|
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
|
-
|
|
1581
|
-
let
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
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 <
|
|
1606
|
-
let token =
|
|
1607
|
-
if (token.eq(
|
|
1608
|
-
const to_delete = i === 0 || i ===
|
|
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
|
-
|
|
1616
|
+
qu[i] = dummy_token;
|
|
1611
1617
|
}
|
|
1612
1618
|
}
|
|
1613
1619
|
}
|
|
1614
|
-
|
|
1615
|
-
for (let i = 0; i <
|
|
1616
|
-
let token =
|
|
1617
|
-
let previous_token = i === 0 ? null :
|
|
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
|
|
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
|
-
|
|
3515
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
3790
|
-
|
|
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.
|
|
3883
|
+
const [inner_args, _] = this.parseGroup(tokens, inner_start + 1, inner_end);
|
|
3794
3884
|
return [
|
|
3795
|
-
new TypstLeftright(lr_token, { body:
|
|
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 [
|
|
3889
|
+
const [inner_args, _] = this.parseGroup(tokens, start + 1, end - 1);
|
|
3800
3890
|
return [
|
|
3801
|
-
new TypstLeftright(lr_token, { body:
|
|
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 =
|
|
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 (
|
|
4019
|
+
if (key in options) {
|
|
3928
4020
|
opt[key] = options[key];
|
|
3929
4021
|
}
|
|
3930
4022
|
}
|